cumakurt/luva

GitHub: cumakurt/luva

Luva 是一款专注于工业控制与 SCADA 网络的离线被动分析工具,通过解析 pcap 文件实现对 OT 资产、拓扑及安全威胁的深度审计与可视化呈现。

Stars: 2 | Forks: 0

# Luva **Luva** 是一款用于工业控制和 SCADA 网络流量捕获(`.pcap`、`.pcapng` 以及 `.gz` 包装的捕获文件)的**被动**离线分析工具。它仅从磁盘读取文件——不进行实时嗅探、不注入、也不与工厂网络交互。 [Türkçe README →](README.tr.md)

Luva interactive HTML report — Executive tab with KPI cards and charts

图:生成的 HTML 报告中的执行摘要(点击 KPI 可进行深入查看)。Luva 离线分析 PCAP/PCAPNG;CLI 运行还会输出 JSON、CSV、GraphML 以及可选的通信图 / NDJSON 导出。

## 功能概览 | 领域 | 能力 | |------|----------------| | **协议** | 对捕获的 Ethernet/IP 流量(通过 **Scapy**)进行**十一**种内置 OT/ICS 协议的深度包检测式解析(见下表)。 | | **资产** | 发现终端(IP、MAC),推断**设备角色**(PLC、HMI、RTU、网关、工程站等),追踪**开放端口**、**ICS 特定字段**(如 Modbus 单元 ID、S7 机架/插槽、DNP3 地址提示)、来自 MAC OUI 的**厂商提示**、通信伙伴、字节/数据包计数,以及带有可读风险因素的**启发式风险评分**。 | | **流** | 构建**双向流**(5 元组式键),将 ICS 帧绑定到流,并聚合统计信息用于报告。 | | **拓扑** | 构建**逻辑网络/服务图**(资产和关系),并导出 **GraphML**(CLI 默认为 **`topology_.graphml`**),适用于 **Gephi**、**yEd** 或 **NetworkX**。 | | **检测** | 基于 YAML 的**规则引擎**,包含 Modbus、S7、DNP3 和通用规则;事件带有**严重性**(INFO → CRITICAL)和**类别**(协议、行为、网络、策略)。统计辅助程序在使用它们的代码路径中支持更丰富的分析。 | | **报告** | **JSON**(完整结构化报告)、**CSV**(资产、流、异常、**审计发现**)、**HTML**(单文件摘要仪表板),以及上述的 **GraphML**。**CLI 运行**会将 **UTC 时间戳**附加到每个工件的基本名称后,以便连续扫描不会覆盖文件(见 [输出文件](#output-files-cli-defaults))。 | | **深度调查** | **`statistics.deep_survey`**:捕获窗口、端口/DNS/HTTP 聚合、TLS 猜测、**`cleartext_hints`**、**`ics_port_visibility`**、Banner、时间线 —— 以及 **OT 敏感明文**启发式规则(见下文)。 | | **威胁导向汇总** | **`statistics.threat_patterns`**:被动提示,如 Modbus 写入流、S7 关键操作族、流上的 IT 远程访问端口、扫描器/凭据特征字符串、ARP/广播噪声、重复的 TCP 负载指纹、顺序多目标 ICS 访问 —— 在 JSON/HTML(**威胁模式**选项卡)中展示,并从 **`audit_workbook`** 进行交叉引用。 | | **OT 明文暴露** | **未加密工业负载**的数据包级检测(Modbus、IEC-104、S7、DNP3、OPC UA HEL、EtherNet/IP、BACnet/IP、SNMP community *已遮蔽*、包含 OT 令牌的 HTTP、通用 ICS TCP 端口)。作为 **`deep_survey.cleartext_ot_sensitive`** 暴露;**Network & cleartext** HTML 表格;汇入 **`pentest_insights`** 和审计发现 **`CLEARTEXT_OT_PAYLOAD`**。[详情](#deep-packet-survey-and-ot-cleartext-hints)。 | | **审计与证据** | JSON 元数据中每个输入文件的**证据保管链**哈希;结构化的 **`audit_workbook`**(发现、MITRE 提示、补救措施、被动暴露指数);**Audit & pentest** 选项卡和 **`audit_findings.csv`**。见 [审计与证据](#audit-and-evidence-for-security-assessors)。 | | **EKS & Purdue** | 嵌入完整的 **ICS/EKS 组件分类法**(现场 → 企业),**每个资产的启发式 EKS 标签**,**Purdue / ISA-95 级别参考**,**IEC 62443 管道说明**,以及 **OT 分段**原则 —— 位于 **`analysis_report.json`** 的 **`eks`** 下和 HTML **EKS & Purdue** 选项卡中。 | | **安全模型** | **被动防护**:分析目标是常规捕获文件;设计上避免实时捕获接口。 | | **隐私** | **`anonymize_ips`**(确定性 IPv4 → 导出文件中的 `10.x.x.x`)和 **`mask_payload`**(在嵌套 JSON 中遮蔽原始负载/十六进制,**包括 OT 明文样本字段**:十六进制预览、HTTP 摘录、SNMP 遮蔽字符串),位于 **`AnalysisConfig`** 和 CLI **`--anonymize-ips`** / **`--mask-payload`**。 | ## CLI(`luva.py` 或 `luva` 命令) 从仓库根目录运行: ``` python3 -m venv .venv source .venv/bin/activate pip install -e . python luva.py capture.pcapng # 或者,在 editable install 之后: luva capture.pcapng ``` 多个捕获文件会合并到一次运行中: ``` python luva.py a.pcap b.pcapng ``` 默认情况:**完整**流水线,所有**十一**种内置解析器,最低严重性 **INFO**,输出位于 **`./reports/`**。流水线运行时,**简短的进度行**会作为 `[Luva] …` 打印到 **stdout**(规则、数据包通过、异常、拓扑、GraphML 路径);除非禁用,否则**数据包计数**仍会输出到 **stderr**。使用 **`--quiet`** 可隐藏 `[Luva]` 行和导出横幅。常用选项: | 选项 | 描述 | |--------|-------------| | `-o`, `--output-dir` | 输出目录(默认:`reports`) | | `--mode` | `full`(完整)、`anomaly-only`(仅异常)、`asset-only`(仅资产)、`topology-only`(仅拓扑) | | `--min-severity` | `INFO`、`LOW`、`MEDIUM`、`HIGH`、`CRITICAL` | | `--protocols` | 逗号分隔的 slug,例如 `modbus,s7` | | `--custom-rules` | 额外的 YAML 规则目录 | | `--formats` | 逗号分隔的输出:`json`、`csv`、`html`、`communication-map`、`anomalies-ndjson` 或 `all`(默认) | | `--chunk-size` | 每个输入文件的最大数据包数(`0` = 完整捕获;适用于快速采样) | | `--compare-baseline` | **之前完整 JSON** 报告的路径(例如 `analysis_report_20260115_083022.json`);将 `statistics.baseline_diff` 添加到新 JSON 中 | | `--anomaly-subset-pcap` | 将异常引用的数据包(`packet_number` + 源文件名)写入此 PCAP | | `--anonymize-ips` / `--mask-payload` | 共享报告的隐私保护 | | `--no-graph` | 跳过拓扑 GraphML 导出 | | `--graph-path` | GraphML 基本路径;**UTC 运行时间戳**会插入到**扩展名之前**(与其他工件后缀相同) | | `--no-progress` | 禁用 stderr 上的数据包进度 | | `-q`, `--quiet` | stdout 上无 `[Luva] …` 阶段行且无 “Writing exports…” 横幅 | | `-v`, `--verbose` | INFO 级别日志 | 示例: ``` luva capture.pcapng -o ./out --mode asset-only --protocols modbus --anonymize-ips luva capture.pcapng --formats json,anomalies-ndjson --chunk-size 50000 ``` 验证规则 YAML(如果 `*.yaml` 中的每个规则都解析成功则退出代码为 `0`;出错则为非零): ``` luva validate-rules /path/to/rules luva validate-rules luva/detection/rules ``` ### 输出文件(CLI 默认) 每次 CLI 运行都会生成一个 **UTC 后缀**,如 **`_YYYYMMDD_HHMMSS`**(例如 `_20260403_153045`),并将其附加到每个导出的**基本名称**扩展名之前。当非空时,JSON **`metadata.report_filename_suffix`** 会重复该后缀。 | 输出 | 描述 | |--------|-------------| | `analysis_report_.json` | 元数据、摘要、资产、流、拓扑、异常、统计信息 —— 包括 **`statistics.deep_survey`**、**`statistics.threat_patterns`**、**`statistics.event_timeline`**、可选的 **`statistics.baseline_diff`**、**`statistics.audit_workbook`**、**`statistics.pentest_insights`** | | `assets_.csv` | 表格化资产 | | `ot_assets_.csv` | OT/ICS 分类资产(ICS 端口、解析器标签、字段提示、推断角色) | | `flows_.csv` | 表格化流 | | `anomalies_.csv` | 表格化检测事件 | | `audit_findings_.csv` | 结构化审计工作表行(ID、严重性、MITRE ID、补救措施、标准参考) | | `anomalies_.ndjson` | 每行一个 JSON 对象(SIEM 友好);使用 `--formats` 启用 | | `_.html` 或 `analysis_report_.html` | HTML 报告(单个输入 → 捕获主干;多个输入 → `analysis_report`):**Executive**(可点击 KPI 深入查看)、**Protocols & exposure**、**OT & comms**、**Assessment**、**Inventory (EKS)**、**OT inventory**、**Audit & pentest**、**Threat patterns**、**Network & cleartext**、**ICS flows**、矩阵、**Anomalies**、**Diagnostics** | | `communication_map_.html` | **交互式 OT 通信图**(发布时从 `luva/output/static/` 嵌入 D3;否则使用 jsDelivr CDN):主机(角色、厂商、风险)、有向链路及**每会话协议**、L4 端口、数据包/字节计数、写入标志;仅 ICS 过滤器;缩放/平移 | | `topology_.graphml` | 用于外部工具的图 | **库/测试:**如果您使用默认的 **`report_filename_suffix=""`** 构造 **`AnalysisConfig`**,报告器将保留**旧版名称**(`analysis_report.json`、`assets.csv` 等)以实现稳定的自动化。 ## 分析模式(CLI 和 Python API) CLI **`--mode`** 标志对应 `AnalysisConfig.mode`: - **`full`** — 资产、流、拓扑、异常 - **`anomaly-only`** — 跳过拓扑/图导出路径 - **`asset-only`** — 仅资产;在流水线允许的情况下跳过异常规则执行 - **`topology-only`** — 侧重拓扑的路径 ## 支持的 ICS / OT 协议 | 协议 | Slug | 典型端口 | 用途 | |----------|------|-----------------|---------------------| | **Modbus/TCP** | `modbus` | 502 | PLC、HMI 和现场设备之间的寄存器/线圈读写;在工业以太网上非常常见。 | | **S7comm** | `s7` | 102 | Siemens S7 会话和循环数据;工程和 PLC I/O。 | | **DNP3** | `dnp3` | 20000 | SCADA/远程站式消息传递(串行 DNP3 的 TCP 适配)。 | | **OPC UA** | `opcua` | 4840 | 客户端-服务器(及发布/订阅)访问标签、报警、方法和历史数据。 | | **EtherNet/IP** | `enip` | 44818 | CIP 封装;Allen-Bradley / Rockwell 式 I/O 和显式消息传递。 | | **IEC 60870-5-104** | `iec104` | 2404 | 基于 TCP 的电力系统远动(APCI/ASDU)。 | | **BACnet/IP** | `bacnet` | 47808 | 楼宇自动化:对象、属性、报警、调度(UDP/TCP;BVLC + APDU 子集)。 | | **MQTT** | `mqtt` | 1883, 8883, 8884, 9001 | 轻量级发布/订阅;IIoT 网关和云链接 SCADA。 | | **SNMP** | `snmp` | 161, 162 | 监控和管理(Get、traps);网络设备和某些嵌入式设备。 | | **Omron FINS** | `omron_fins` | 9600 | 基于 TCP/UDP 的工厂接口网络服务(被动帧子集)。 | | **GE SRTP** | `ge_srtp` | 18245, 18246 | 服务请求传输协议信封(被动子集)。 | **默认启用的 slug**(同样的十一个):`modbus`、`s7`、`dnp3`、`opcua`、`enip`、`iec104`、`bacnet`、`mqtt`、`snmp`、`omron_fins`、`ge_srtp`。使用 **`--protocols`** 限制该集合。 端口注册表仍然列出其他 OT 相关端口(例如 PROFINET、Foundation Fieldbus HSE)仅作为提示;除非在上表中列出,否则它们在本包中没有专用解析器。 ## 自定义规则 在 CLI 上使用 **`--custom-rules /path/to/dir`**,或在代码中设置 **`AnalysisConfig.custom_rules_dir`**。文件必须匹配内置规则模式(见 `luva/detection/rules/*.yaml`)。在部署自定义规则之前,使用 **`luva validate-rules DIR`** 检查文件。 ## Python API(库使用) ``` from pathlib import Path from luva.core.config import AnalysisConfig, AnalysisMode, utc_report_filename_suffix from luva.core.pipeline import AnalysisPipeline cfg = AnalysisConfig( input_files=[Path("capture.pcapng")], mode=AnalysisMode.FULL, export_formats=("json", "csv"), # omit keys you do not need chunk_size=0, compare_baseline=None, # e.g. Path("prior_report.json") anomaly_subset_pcap=None, # e.g. Path("anomaly_hits.pcap") report_filename_suffix=utc_report_filename_suffix(), # optional: match CLI timestamped names quiet=False, # set True to silence [Luva] stdout lines during run() ) result = AnalysisPipeline(cfg).run() # result.assets, result.flows, result.anomalies, result.to_dict() ``` ## 面向安全评估人员的审计与证据 被动运行包含面向从离线捕获进行工作的**审查员、审计员和渗透测试人员**的工件: | 工件 | 位置 | 用途 | |----------|--------|---------| | **输入完整性** | `metadata.input_evidence` | 每个文件:路径、文件名、大小、**磁盘存储字节**的 **SHA-256**(例如 `.pcap.gz` 是压缩后哈希)。`metadata.evidence_integrity_note` 说明如何将其用于**证据保管链**(复制或归档后重新哈希)。 | | **审计工作表** | `statistics.audit_workbook` | 结构化发现(`AUD-001` …)及其严重性、类别、叙述、证据摘要、**MITRE ATT&CK** 映射*提示*、补救文本,以及 **IEC 62443 / NIST SP 800-82** 风格参考;**被动暴露指数**(0–100,加权严重性 —— **不是** CVSS 评分);**ICS 写入**流样本;**公共 IPv4** 资产;具有 **22 / 3389 / 5900** 的资产;与 `pentest_insights` 的交叉引用。 | | **HTML** | **Audit & pentest** 选项卡 | 哈希、范围/局限性、发现表和表面样本的可读摘要。 | | **CSV** | `audit_findings_.csv`(CLI)或 `audit_findings.csv`(如果 `report_filename_suffix` 为空) | 工作表发现的平面导出,与其他 CSV 并存。 | MITRE 和 IEC/NIST 参考是**来自被动流量的启发式标签** —— 在将其视为确认技术或合规差距之前,请务必根据您的**架构**、**CMDB** 和**威胁模型**进行确认。 ## 深度数据包调查和 OT 明文提示 流水线始终从被动元数据构建 **`statistics.deep_survey`**:捕获跨度、唯一 IP/MAC、热门 TCP/UDP 端口(带注册表标签)、每分钟时间线、DNS qname、HTTP `Host` 头、**`cleartext_hints`**(SSH/FTP/telnet/SNMP/HTTP 类计数)、**`banner_samples`**、**`tls`** 启发式规则以及 **`ics_port_visibility`**。 **`cleartext_ot_sensitive`** 会在负载看起来像**未加密的 OT 流量**时添加**有界的、数据包级**的行(默认最多 **40** 个去重样本)(跳过 TLS ClientHello)。类别包括但不限于:端口 502 上的 **Modbus TCP**(MBAP + 功能码;写入族 FC 评分更高)、**IEC-104** APCI 形状帧、端口 102 上的 **S7** TPKT、端口 20000 上的 **DNP3**、端口 4840 上的 **OPC UA** `HEL`、端口 44818 上的 **EtherNet/IP** 注册会话、UDP 47808 上的 **BACnet/IP** BVLC、**SNMPv1/v2c** community 字段(样本中**已遮蔽**,例如 `p*******c`)、包含 OT 相关令牌的 **HTTP** 请求/响应,以及来自端口注册表的其他 ICS 相关 TCP 端口上的**通用非 TLS 负载**。 每个样本包括端点、简短**摘要**和**截断的十六进制预览**或文本摘录。**`hits_by_category`** 计算每个匹配的数据包;样本按类别和流键去重。 - **JSON**: `statistics.deep_survey.cleartext_ot_sensitive` - **HTML**: **Network & cleartext** → *OT-sensitive cleartext (heuristic)* - **`pentest_insights`**: 被动发现和 **`summary_counts.cleartext_ot_packet_hits`** - **`audit_workbook`**: 当存在命中或样本时的 **`CLEARTEXT_OT_PAYLOAD`** 在共享导出文件之前使用 **`--mask-payload`** 以去除 `evidence_preview_hex`、`http_context_excerpt` 和 `snmp_community_redacted`。这些规则是**启发式的**(TCP 分段、隧道和非标准端口可能导致误报或噪声);请在 PCAP 中验证可疑行。 ## 系统要求 - **Python 3.10+** - 依赖项:**Scapy**、**NetworkX**、**Rich**、**Typer**、**PyYAML**、**Jinja2**、**NumPy**(见 `pyproject.toml`) ### 超大型捕获(例如多 GB / ~20 GB) Luva 从磁盘流式传输数据包(RAM 中无完整捕获)。每流状态使用**固定内存**统计(Welford)而不是存储每个数据包的大小或到达间隔时间。时间线桶按**每分钟**计算;DNS/HTTP 基数有上限。**默认情况下**(`AnalysisConfig` / CLI 无大小覆盖),导出的流、通信矩阵和 **`communication_graph`** 边**无上限**(`max_flows_export`、`max_communication_matrix_ips` 和 `max_communication_graph_edges` 默认为 **`0`** = 包含所有内容 —— 完整报告)。如果需要更小的 JSON/HTML/CSV,请在 `AnalysisConfig` 上设置正限制。进度默认每 2 M 个数据包打印到 **stderr**(`show_progress`、`progress_packet_interval`);阶段摘要使用 **stdout** 作为 `[Luva] …`,除非 `quiet=True`。**`.pcap.gz`** 仍需足够的**磁盘**空间来保存解压后的临时文件。 ## 样本 示例捕获文件位于 `luva/sample/`。许多 `.pcap` 文件在运行 `git lfs pull` 之前可能是 **Git LFS 指针**;某些 `.pcapng` 文件是常规二进制文件。 ### 公开协议冒烟测试捕获(`public_pcaps/`) 小型**第三方** PCAP(Wireshark SampleCaptures + [w3h/icsmaster](https://github.com/w3h/icsmaster))一次性覆盖 **Modbus、S7、DNP3、OPC UA、EtherNet/IP 和 IEC 104**;使用您自己的捕获来演练 **BACnet、MQTT 和 SNMP** 解析器。 ``` ./public_pcaps/fetch_public_pcaps.sh # optional: re-download from the network python luva.py public_pcaps/*.pcap ``` 归属和上游 URL 列于 [`public_pcaps/SOURCES.txt`](public_pcaps/SOURCES.txt)。 ## 开发 ``` pip install -e ".[dev]" pytest luva/tests ot_baseline/tests ruff check luva ot_baseline luva.py baseline.py mypy luva ``` **CI:**[GitHub Actions](.github/workflows/ci.yml) 在 Python 3.10、3.12 和 3.13 上运行 **pytest**、**ruff** 和 **mypy**,针对 `main` / `master` 的推送和拉取请求。 **贡献:**见 [`CONTRIBUTING.md`](CONTRIBUTING.md)。**安全联系方式:**[`SECURITY.md`](SECURITY.md)。 ### GitHub 发布检查清单 | 项目 | 位置 | |------|-----------| | 开源许可证 | [`LICENSE`](LICENSE)(AGPL-3.0-only,在 `pyproject.toml` 中引用) | | 项目元数据和 URL | [`pyproject.toml`](pyproject.toml)(`Homepage`、`Repository`) | | 忽略本地/构建噪声 | [`.gitignore`](.gitignore) | | 自动化测试和 Lint | [`.github/workflows/ci.yml`](.github/workflows/ci.yml) | | 贡献者和安全说明 | [`CONTRIBUTING.md`](CONTRIBUTING.md), [`SECURITY.md`](SECURITY.md) | | README 截图 | [`img/main.png`](img/main.png) | ## 作者 由 **Cuma KURT** 开发 — [cumakurt@gmail.com](mailto:cumakurt@gmail.com) - [LinkedIn](https://www.linkedin.com/in/cuma-kurt-34414917/) - [GitHub repository](https://github.com/cumakurt/luva) ## 许可证 Luva 基于 **GNU Affero General Public License v3.0 only**(**AGPL-3.0-only**)授权。详见 [`LICENSE`](LICENSE) 文件。如果您将**修改**版本作为**网络服务**运行,AGPL 要求您向与该服务交互的用户提供相应的源代码。
标签:AMSI绕过, AWS, DNP3, DPI, Gephi, GraphML, HMI, HTML 报告, ICS, NTLM Relay, osquery, OT 安全, PCAP 分析, PKINIT, PLC 识别, Python, SCADA 安全, Scapy, YAML, 云计算, 威胁检测, 安全库, 工业协议, 工控安全, 无后门, 流量捕获, 深度包检测, 特权检测, 离线分析, 网络安全分析, 网络拓扑, 被动分析, 规则引擎, 运维安全, 逆向工具, 防御绕过, 风险评分