arnavparekar/ics-watchdog

GitHub: arnavparekar/ics-watchdog

面向工业控制网络的轻量级容器化被动安全监控工具,通过有状态规则引擎实时检测 Modbus/TCP 异常流量并将告警映射至 MITRE ATT&CK for ICS 框架。

Stars: 0 | Forks: 0

# ICS-Watchdog [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Python 3.11](https://img.shields.io/badge/python-3.11-3776AB?logo=python&logoColor=white)](https://www.python.org/) [![Docker](https://img.shields.io/badge/docker-ready-2496ED?logo=docker&logoColor=white)](https://www.docker.com/) [![Scapy](https://img.shields.io/badge/capture-scapy-ff69b4)](https://scapy.net/) [![MITRE ATT&CK for ICS](https://img.shields.io/badge/MITRE-ATT%26CK%20for%20ICS-red)](https://attack.mitre.org/matrices/ics/) 一个针对操作技术(OT)网络的轻量级、容器化**被动安全监控器**。ICS-Watchdog 实时嗅探 Modbus/TCP 流量,且不会产生任何探测数据包。它对捕获的数据运行有状态的规则引擎,并生成结构化的 JSON + HTML 安全报告,其中的检测结果映射到 [MITRE ATT&CK® for ICS](https://attack.mitre.org/matrices/ics/) 框架。 ## 目录 - [背景](#background) - [项目架构](#project-architecture) - [目录结构](#directory-structure) - [检测规则](#detection-rules) - [攻击场景](#attack-scenarios) - [快速开始](#quick-start) - [输出示例](#sample-output) - [工作原理](#how-it-works) - [许可证](#license) ## 背景 工业控制系统(ICS)和操作技术(OT)网络——如工厂车间、电网、水处理厂——运行的协议都是为了可靠性而设计的,**而非安全性**。Modbus/TCP 作为部署最广泛的 ICS 协议之一,在传输所有命令时均采用明文形式,没有身份验证,也没有来源验证。 合法的 Modbus Master(HMI/SCADA)每隔几秒钟就会向 PLC 和 RTU 发送 `Read Holding Registers (FC03)` 请求。攻击者一旦接入同一网段,就可以发送外观相同的数据包来强制开启阀门、禁用安全联锁装置,或者重放几分钟前捕获的命令序列——而 Modbus 协议本身对此毫无防范。 **ICS-Watchdog** 通过被动部署在网络线路上,学习什么是“正常”状态,并在流量偏离预期行为的瞬间发出映射至 MITRE 的警报,从而解决了这个问题。 ## 项目架构 本项目以完整、独立的 Docker Compose 环境提供。它不仅包含监控引擎,还包含一整套**模拟 ICS 蜜罐**和**自动化攻击注入器**,因此无需真实硬件即可对检测规则进行演练和验证。 ``` graph TD subgraph "Docker Network (ics-net: 192.168.100.0/24)" M[Modbus Master
192.168.100.10] S1[Modbus Slave 1
192.168.100.21] S2[Modbus Slave 2
192.168.100.22] S3[Modbus Slave 3
192.168.100.23] I[Attack Injector
192.168.100.50] W[ICS-Watchdog
192.168.100.100] R[Report Generator
192.168.100.200] end M <-->|Modbus/TCP Polling| S1 M <-->|Modbus/TCP Polling| S2 M <-->|Modbus/TCP Polling| S3 I -.->|Malicious Traffic| W I -.->|Probing| S1 W == "Passive Sniffing (Scapy)" ==> W W -->|Writes JSON Logs| V[(Docker Volume)] V -->|Reads JSON Logs| R R -->|Generates| HTML[HTML Dashboard] ``` ### 容器 | 容器 | IP | 角色 | |---|---|---| | `ics-master` | `192.168.100.10` | 合法 Modbus Master —— 使用 FC03 每秒轮询从站,模拟真实的 SCADA/HMI 工作站。 | | `ics-slave-1` | `192.168.100.21` | Modbus/TCP 服务器 (PLC/RTU)。包含 10 个带有模拟传感器数据的保持寄存器。 | | `ics-slave-2` | `192.168.100.22` | Modbus/TCP 服务器 (PLC/RTU)。 | | `ics-slave-3` | `192.168.100.23` | Modbus/TCP 服务器 (PLC/RTU)。 | | `ics-injector` | `192.168.100.50` | 攻击注入器。启动时保持空闲状态;通过 `docker exec` 手动触发攻击。 | | `ics-watchdog` | `192.168.100.100` | 核心安全引擎。在混杂模式下运行 Scapy,解析 Modbus/TCP MBAP 标头,将数据包送入有状态规则引擎,并将 `alerts.jsonl` 和 `packet_stats.json` 写入共享卷。 | | `ics-reporter` | `192.168.100.200` | 按需报告生成器。读取共享卷并生成 `watchdog_report.json` 和 `watchdog_report.html` 仪表板。 | ## 目录结构 ``` ICS-Watchdog/ ├── docker-compose.yml # Defines all 7 containers and the shared volume │ ├── master/ │ ├── Dockerfile │ └── master.py # pymodbus async client; polls slaves with FC03 every 1s │ ├── slave/ │ ├── Dockerfile │ └── slave.py # pymodbus async server; SLAVE_ID set via env var │ ├── watchdog/ │ ├── Dockerfile │ ├── watchdog.py # Scapy sniffer, packet parser, stats writer │ ├── rules.py # Stateful rule engine — R-001 through R-008 │ └── alert.py # Alert dataclass + JSONL writer │ ├── injector/ │ ├── Dockerfile │ ├── inject.py # CLI entry-point: --attack {recon,coil-inject,replay} │ └── scenarios/ │ ├── recon.py # Reconnaissance scan (T0846, T0843) │ ├── coil_inject.py # Unauthorised coil write burst (T0855) │ └── replay.py # Raw Scapy packet replay (T0856) │ ├── reporter/ │ ├── Dockerfile │ ├── report.py # Reads JSONL logs, renders JSON + HTML report │ └── templates/ │ └── report.html.j2 # Jinja2 template — dark dashboard with Chart.js │ └── samples/ ├── watchdog_report.json # Example JSON report generated from a live test run └── watchdog_report.html # Example HTML dashboard (open in any browser) ``` ## 检测规则 [`watchdog/rules.py`](watchdog/rules.py) 中的规则引擎是**有状态的**——它会在跨数据包的范围内维护每个源 IP 的滑动窗口计数器,而不仅仅针对单个数据包。这使得它既能检测缓慢潜伏的侦察行为,也能检测突发攻击。 | 规则 | 名称 | 严重程度 | MITRE 技术 | 触发条件 | |------|------|----------|-----------------|-------------------| | **R-001** | Modbus Function Code 扫描 | HIGH | [T0846](https://attack.mitre.org/techniques/T0846/) Remote System Discovery | 30 秒内来自同一源 IP 的 >10 个**不同** function code。正常设备通常使用 1-2 个 FC。 | | **R-002** | 未授权的 Coil 写入 | CRITICAL | [T0855](https://attack.mitre.org/techniques/T0855/) Unauthorized Command Message | 来自已知 Master 以外任何 IP 的 FC05 (Write Single Coil) 或 FC15 (Write Multiple Coils)。 | | **R-003** | 寄存器读取泛洪 | HIGH | [T0884](https://attack.mitre.org/techniques/T0884/) Connection Probe | 10 秒内来自同一源的 >50 个 FC03 Read Holding Registers 请求。 | | **R-004** | 顺序扫描探测 | HIGH | [T0846](https://attack.mitre.org/techniques/T0846/) Remote System Discovery | Modbus 数据包发送至广播地址,或在 5 秒内顺序探测所有从站的 Unit ID {1, 2, 3}。 | | **R-005** | 越界寄存器访问 | MEDIUM | [T0855](https://attack.mitre.org/techniques/T0855/) Unauthorized Command Message | 请求寄存器地址 > 9 的 FC03/FC04 请求(超出配置的 PLC 数据模型范围)。 | | **R-006** | 新源 IP | HIGH | [T0843](https://attack.mitre.org/techniques/T0843/) Program Download | 来自不在静态允许列表 `{10, 21, 22, 23}` 中的 IP 的 Modbus 流量。每个新 IP 触发一次。 | | **R-007** | 重放攻击 | CRITICAL | [T0856](https://attack.mitre.org/techniques/T0856/) Spoof Reporting Message | 相同的 Modbus payload(逐字节一致,包含 function code)在 5 秒内被同一源重传 >3 次。 | | **R-008** | 过度写入速率 | CRITICAL | [T0855](https://attack.mitre.org/techniques/T0855/) Unauthorized Command Message | 10 秒内来自单一源的 >20 个写入类命令 (FC05/FC06/FC15/FC16)。 | ## 攻击场景 `injector` 容器中包含了三个攻击场景。每个场景都是独立的,并将进度打印到 stdout。 ### 1. 侦察扫描 — `recon` 模拟攻击者刚刚获得网络访问权限,并试图摸清 ICS 拓扑结构。 - 连接到三个从站 IP 中的每一个,并发送 8 个 pymodbus function code (FC01–FC06, FC15, FC16)。 - 使用 Scapy 向 watchdog 镜像 4 个额外的原始 function code (FC07, FC08, FC17, FC23) —— 将不同的 FC 计数推高至 R-001 的阈值 10 以上。 - 请求每个从站上的寄存器地址 50(超出 0–9 的限制),以触发 R-005。 - 注入器 IP (`192.168.100.50`) 不在允许列表中,因此 R-006 会在第一个数据包上触发。 - 在依次探测所有三个 Unit ID 后,R-004 触发。 **预期触发的规则:** R-001, R-004, R-005, R-006 ### 2. Coil 写入注入 — `coil-inject` 模拟攻击者试图通过覆盖 coil 状态来驱动物理继电器或阀门。 - 以 50 毫秒的间隔,快速向 slave-1 发送 30 条 FC05 (Write Single Coil) 命令。 - 同时将每次写入镜像给 watchdog 进行捕获。 - 在约 1.5 秒内的 30 次写入会触发 R-008(>20 次写入 / 10 秒)。 - 每次单独的写入都会触发 R-002(非 Master 向 coil 写入)。 **预期触发的规则:** R-002, R-008 ### 3. 重放攻击 — `replay` 模拟攻击者捕获了合法的 Modbus FC06 (Write Single Register) 命令,并正在逐字重传它以篡改设定点。 - 使用 **Scapy**(而非 pymodbus)精心构造原始的 Modbus/TCP 数据包,使得 payload 在所有传输中逐字节完全一致——从而绕过 pymodbus 自动递增的 transaction ID。 - 以 300 毫秒的间隔,向 watchdog 发送 10 个相同的数据包。 - 在 5 秒窗口内出现第 4 个相同 payload 后,R-007 触发。 **预期触发的规则:** R-007 ## 快速开始 **前提条件:** Docker Desktop (Mac/Windows) 或 Docker Engine + Compose (Linux)。 ### 1. 克隆并启动技术栈 ``` git clone https://github.com/arnavparekar/ics-watchdog.git cd ics-watchdog docker-compose up -d --build ``` 这会构建所有镜像并启动 7 个容器。Watchdog 会立即开始嗅探。Master 会立即开始轮询从站。您将立即在 watchdog 日志中看到合法的轮询流量: ``` docker logs -f ics-watchdog # 2026-06-08 10:38:36 [WATCHDOG] INFO 正在启动 packet capture…(按 Ctrl+C 停止) # 2026-06-08 10:38:37 [WATCHDOG] INFO REQ 192.168.100.10 → 192.168.100.21 FC=03 (Read Holding Registers) reg=0 cnt=10 ``` ### 2. 运行攻击场景 打开第二个终端并触发注入器: ``` # Reconnaissance — 扫描网络,fuzzes function codes docker exec ics-injector python3 inject.py --attack recon # Coil injection — 30 次快速未授权写入 docker exec ics-injector python3 inject.py --attack coil-inject # Replay — 重传相同的 raw packet 10 次 docker exec ics-injector python3 inject.py --attack replay ``` 您应该会看到警报实时出现在 watchdog 日志中: ``` [WATCHDOG] WARNING 🚨 ALERT FIRED: [R-006] New Source IP (src: 192.168.100.50) [WATCHDOG] WARNING 🚨 ALERT FIRED: [R-005] Out-of-Range Register Access (src: 192.168.100.50) [WATCHDOG] WARNING 🚨 ALERT FIRED: [R-001] Modbus Function Code Scan (src: 192.168.100.50) [WATCHDOG] WARNING 🚨 ALERT FIRED: [R-004] Sequential Scan Probe (src: 192.168.100.50) [WATCHDOG] WARNING 🚨 ALERT FIRED: [R-002] Unauthorised Write to Coils (src: 192.168.100.50) [WATCHDOG] WARNING 🚨 ALERT FIRED: [R-008] Excessive Write Rate (src: 192.168.100.50) [WATCHDOG] WARNING 🚨 ALERT FIRED: [R-007] Replay Attack (src: 192.168.100.50) ``` ### 3. 生成报告 ``` docker run --rm \ -v ics-watchdog_watchdog-data:/app/output \ ics-watchdog-report-gen \ python3 report.py ``` 这会输出: ``` JSON report generated at /app/output/watchdog_report.json HTML report generated at /app/output/watchdog_report.html ``` 将 HTML 文件从卷中复制出来以进行查看: ``` docker run --rm -v ics-watchdog_watchdog-data:/app/output \ ics-watchdog-report-gen cat /app/output/watchdog_report.html > watchdog_report.html open watchdog_report.html # macOS — or just open the file in any browser ``` ### 4. 卸载清理 ``` docker-compose down -v # -v removes the data volume too ``` ## 输出示例 真实测试运行生成的报告文件位于 [`samples/`](samples/) 目录中: - [`samples/watchdog_report.json`](samples/watchdog_report.json) —— 机器可读的摘要和完整警报日志。 - [`samples/watchdog_report.html`](samples/watchdog_report.html) —— 在浏览器中打开此文件以查看仪表板(可离线工作;无需服务器)。 HTML 仪表板包含: - **4 张摘要指标卡片** —— 运行持续时间、总数据包数、Modbus 数据包数、总警报数。 - **严重程度甜甜圈图** —— CRITICAL / HIGH / MEDIUM / LOW 细分。 - **Function code 柱状图** —— 可视化展示在网络上观察到的 Modbus FC。 - **完整警报表** —— 每个触发的警报,包含时间戳、严重程度徽章、规则 ID、MITRE 技术、源/目标 IP 以及简明的英文解释。 ## 工作原理 ### 被动捕获 (`watchdog/watchdog.py`) Scapy 的 `sniff()` 在绑定到 `eth0` 的后台线程中运行,使用 BPF 过滤器 `tcp port 502`。对于每个数据包: 1. 提取 IP/TCP 层。 2. 原始 TCP payload 传递给 `parse_mbap()`,该函数会验证 Modbus Application Protocol 标头(6 字节固定标头:Transaction ID、Protocol ID、Length、Unit ID)并提取 function code 和 PDU 数据。 3. 解析后的数据包字典会扩充 `src_ip`、`dst_ip`、时间戳和方向(基于目标端口的 REQ 与 RSP)信息。 4. 只有请求(方向 = REQ)会由规则引擎评估——响应仅记录用于统计。 ### 规则引擎 (`watchdog/rules.py`) `RuleEngine.evaluate(pkt)` 按顺序通过所有 8 个规则方法处理数据包。每个规则方法都是**有状态的**——它维护着 `defaultdict` 结构(以 `src_ip` 为键的滑动窗口),这些结构在 watchdog 进程的整个生命周期内跨数据包持久存在。当越过阈值时,规则返回一个 `Alert` 对象,否则返回 `None`。 ### 警报输出 (`watchdog/alert.py`) 每个 `Alert` 都会被序列化为 `/app/output/alerts.jsonl` 中的一行 JSONL(每行一个 JSON 对象)。这种选择是刻意为之——它是仅追加的,并且在写入时并发读取是安全的。 ### 报告生成 (`reporter/report.py`) 从共享卷中读取 `alerts.jsonl` 和 `packet_stats.json`,将其聚合为一个单独的报告字典,序列化为 `watchdog_report.json`,然后将其传递给 Jinja2 模板 (`report.html.j2`) 以生成 `watchdog_report.html`。 ## 许可证 本项目基于 MIT 许可证授权——详情请参阅 [LICENSE](LICENSE) 文件。
标签:Docker, OT网络监控, PKINIT, Python, SCADA, 哈希传递, 安全防御评估, 工控安全, 无后门, 时间线生成, 版权保护, 被动监听, 请求拦截, 逆向工具