
防御的セキュリティデモ: 脆弱なICSをCVE-2019-14462から保護するseL4マイクロカーネルゲートウェイ
産業用制御システムをサイバー攻撃から保護するためのプロトコルブレーク方式とパケットフォワーディング方式のアーキテクチャを比較する、防御的セキュリティ研究プロジェクトです。
ドキュメント:
- ネットワークアーキテクチャ - ネットワーク図とトラフィックフロー
- コンテナアーキテクチャ - Dockerコンテナの関係性
- CVE解説 - 脆弱性の詳細と攻撃メカニズム
現代のICS/SCADAシステムは、FrostyGoopのような高度な攻撃に直面しています。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 LoC(マイクロカーネル) | ~500,000 LoC(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
注記: デフォルトの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〜109ですwrite_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の盲目化ではなく、完全なサービス拒否(DoS)です。
Linux netfilter nf_tables(カーネルv5.14〜v6.6)のuse-after-freeにより、コンテナエスケープが可能になります:
# 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コンテナはホストカーネルを共有します。攻撃者が(例えばCVE-2022-20685を介して)Snortを侵害した場合、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構成が用意されています:
# 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
sudo docker compose --profile snort-combined up -d
./cve_tools/cve_0367_attack 127.0.0.1 503 # Test detection
sudo docker compose --profile snort-combined down
Client ────TCP────► Snort ────TCP────► PLC
(same connection flows through)
Client ────TCP1────► seL4 ────TCP2────► PLC
(terminates, validates, new connection)
PLCは、マルチマスター対応の地域暖房コントローラをシミュレートします(現代のPLCに典型的なスレッド・パー・クライアントアーキテクチャ)。
| モード | サービス | ポート | 説明 |
|---|---|---|---|
| CVE-2022-0367 | plc(デフォルト) | 5020 | ASANビルド、アドレス100〜109のレジスタ |
# Default mode (CVE-2022-0367 with ASAN)
sudo docker compose up plc
# CVE-2019-14462 mode
sudo docker compose --profile cve14462 up plc-14462
CVE-2022-0367モードでは、レジスタはアドレス100〜109にあります(SCADAツールではアドレス40101を使用):
このプロジェクトには、防御的セキュリティ研究のための意図的に脆弱なコードが含まれています。脆弱なlibmodbus 3.1.2およびSnort 2.9.18は、セキュリティの概念を実証するために含まれています。
本番環境に無防備なコンポーネントをデプロイしないでください。
認可された防御的セキュリティ研究および教育目的にのみ使用できます。
| ポート | 経路 | アーキテクチャ | 保護 |
|---|
| 502 | クライアント → seL4 → PLC | プロトコルブレーク | Modbus構造を検証 |
| 503 | クライアント → Snort → PLC | パケットフォワーディング | ルールベースIDS |
| 5020 | クライアント → PLC (ASAN) | 直接 | CVE-2022-0367モード |
| 5022 | クライアント → PLC | 直接 | CVE-2019-14462モード (プロファイル: cve14462) |
| プロファイル | コマンド | ルール数 | 説明 |
|---|
| default | docker compose up snort | 12 | Quickdraw(業界標準) |
| quickdraw | --profile snort-quickdraw | 12 | Digital Bond Quickdrawのみ |
| talos | --profile snort-talos | 40 | modbus_funcキーワードを使用したTalosスタイル |
| modbus | --profile snort-modbus | 13 | カスタムCVE検出ルールのみ |
| combined | --profile snort-combined | 65 | 全ルールの組み合わせ |
| CVE | Quickdraw | Talos | カスタムModbus |
|---|
| CVE-2019-14462 (MBAP長さ) | ✅ | ✅ | ✅ |
| CVE-2022-0367 (ヒープアンダーフロー) | ❌ | ✅ | ✅ |
| CVE-2022-20685 (Snort DoS) | ❌ | ✅ | ✅ |
| 書き込み操作 | ❌ | ✅ | ✅ |
| 偵察 | ✅ | ✅ | ✅ |
| DoS機能コード | ✅ | ✅ | ❌ |
| ディレクトリ | 説明 |
|---|
gateway/ | seL4ゲートウェイコンテナ(QEMU + seL4カーネル) |
snort/ | 複数のルールプロファイルを備えたSnort 2.9.18 IDS(CVE-2022-20685に対して脆弱) |
plc/ | 地域暖房シミュレーション(脆弱なlibmodbus 3.1.2、CVE-2022-0367モード) |
cve_tools/ | CVEデモンストレーション用の攻撃ツール |
scripts/ | ユーティリティおよび実験スクリプト |
docs/ | Mermaid図を含むドキュメント(ネットワーク、コンテナ、CVE) |
| ツール | 対象 | 説明 |
|---|
cve_14462_attack | libmodbus | MBAP長さの不一致によるヒープバッファオーバーフロー |
cve_0367_attack | libmodbus | 無効な書き込みアドレスによるヒープアンダーフロー(FC 0x17) |
cve_20685_attack | Snort | Modbusプリプロセッサの無限ループ(DoS) |
cve-2024-1086/ | Linuxカーネル | nf_tablesによる権限昇格(コンテナエスケープ) |
| CVE-2019-14462 | plc-14462 | 5022 | 通常ビルド、アドレス0〜9のレジスタ |
| アドレス | レジスタ | 説明 | R/W |
|---|
| 100 | HR[0] | 室内温度(°C ÷10) | R |
| 101 | HR[1] | バルブ指令(0〜100%) | R/W |
| 102 | HR[2] | 温度設定値(°C ÷10) | R/W |
| 103 | HR[3] | モード(0=手動、1=自動) | R/W |
| 104 | HR[4] | 外気温度(°C ÷10) | R |
| 105-109 | HR[5-9] | ステータス、位置、供給温度、稼働時間、電力 | R |