
Demo di sicurezza difensiva: gateway microkernel seL4 che protegge gli ICS vulnerabili da CVE-2019-14462
Un progetto di ricerca difensiva sulla sicurezza che confronta le architetture protocol-break e packet-forwarding per la protezione dei sistemi di controllo industriale dagli attacchi informatici.
Documentazione:
- Architettura di rete - Diagrammi di rete e flusso del traffico
- Architettura dei container - Relazioni tra i container Docker
- Spiegazioni delle CVE - Dettagli delle vulnerabilità e meccanismi d'attacco
I moderni sistemi ICS/SCADA affrontano attacchi sofisticati come FrostyGoop, che ha preso di mira i sistemi di teleriscaldamento ucraini tramite Modbus TCP nel gennaio 2024, lasciando oltre 600 famiglie senza riscaldamento durante temperature sotto lo zero. Le soluzioni di sicurezza tradizionali (firewall, IDS) utilizzano architetture packet-forwarding che ispezionano il traffico in linea ma mantengono un'unica connessione TCP end-to-end.
Questo progetto dimostra un'alternativa: un gateway protocol-break basato sul microkernel formalmente verificato seL4 microkernel. Terminando le connessioni TCP e validando la semantica del protocollo prima di stabilire nuove connessioni verso i dispositivi protetti, questa architettura offre garanzie di sicurezza più forti.
| Aspetto | Protocol-Break (seL4) | Packet-Forwarding (Snort) |
|---|---|---|
| CVE-2019-14462 | BLOCCATO (validazione lunghezza) | RILEVATO (regole Quickdraw) |
| CVE-2022-0367 | BLOCCATO (validazione indirizzo) | RILEVATO (regole personalizzate) |
| CVE-2022-20685 | IMMUNE (nessun preprocessore) | VULNERABILE (DoS IDS) |
| CVE-2024-1086 | IMMUNE (nessun kernel Linux) | VULNERABILE (condivide il kernel dell'host) |
| Varianti sconosciute | BLOCCATE (validazione strutturale) | NON RILEVATE (nessuna firma) |
| Attacchi allo stato TCP | BLOCCATI (connessione terminata) | Possibili |
| Superficie d'attacco | ~1.000 LoC (microkernel) | ~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
| Porta | Percorso | Architettura | Protezione |
|---|---|---|---|
| 502 | Client → seL4 → PLC | Protocol-break | Valida la struttura Modbus |
| 503 | Client → Snort → PLC | Packet-forwarding | IDS basato su regole |
| 5020 | Client → PLC (ASAN) | Diretto | Modalità CVE-2022-0367 |
| 5022 | Client → PLC | Diretto | Modalità CVE-2019-14462 (profilo: cve14462) |
Nota: Il PLC predefinito ora esegue la modalità CVE-2022-0367 con ASAN. Usa
--profile cve14462per i test di CVE-2019-14462.
Il PLC utilizza libmodbus 3.1.2 volutamente vulnerabile. L'attacco sfrutta i campi di lunghezza MBAP considerati affidabili:
# 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
Un bug nel controllo dei limiti in modbus_mapping_new_start_address() consente un heap underflow tramite il codice di funzione 0x17 (Write and Read Registers):
# 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
Dettagli tecnici:
start_registers=100, gli indirizzi validi sono 100-109write_address < 100, causando un indice di array negativomb_mapping, inclusi i puntatoriSnort 2.9.18 ha un integer overflow nel suo preprocessore Modbus che causa un ciclo infinito, bloccando completamente tutto il traffico attraverso l'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
Perché è devastante: Snort usa la modalità inline NFQUEUE, il che significa che i pacchetti vengono mantenuti nella coda del kernel finché Snort non restituisce un verdetto. Quando Snort si blocca, non vengono restituiti verdetti e TUTTO il traffico si ferma - non solo cecità dell'IDS, ma denial of service completo.
Un use-after-free in Linux netfilter nf_tables (kernel v5.14-v6.6) consente la fuga dal container:
# 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/
Perché è importante: I container Docker condividono il kernel dell'host. Se un attaccante compromette Snort (ad es. tramite CVE-2022-20685), potrebbe usare CVE-2024-1086 per fuggire dal container e ottenere i privilegi di root sull'host. seL4 è immune perché esegue un microkernel minimale, non 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
Sono disponibili più configurazioni di Snort per il benchmarking dell'efficienza di rilevamento:
| Profilo | Comando | Regole | Descrizione |
|---|---|---|---|
| default | docker compose up snort | 12 | Quickdraw (standard di settore) |
| quickdraw | --profile snort-quickdraw | 12 | Solo Digital Bond Quickdraw |
| talos | --profile snort-talos | 40 | Stile Talos con parole chiave modbus_func |
| modbus | --profile snort-modbus | 13 | Solo regole personalizzate di rilevamento CVE |
| combined | --profile snort-combined | 65 | Tutte le regole combinate |
# 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
| CVE | Quickdraw | Talos | Modbus personalizzato |
|---|---|---|---|
| CVE-2019-14462 (lunghezza MBAP) | ✅ | ✅ | ✅ |
| CVE-2022-0367 (heap underflow) | ❌ | ✅ | ✅ |
| CVE-2022-20685 (DoS di Snort) | ❌ | ✅ | ✅ |
| Operazioni di scrittura | ❌ | ✅ | ✅ |
| Ricognizione | ✅ | ✅ | ✅ |
| Codici di funzione DoS | ✅ | ✅ | ❌ |
Client ────TCP────► Snort ────TCP────► PLC
(same connection flows through)
Client ────TCP1────► seL4 ────TCP2────► PLC
(terminates, validates, new connection)
| Directory | Descrizione |
|---|---|
gateway/ | Container del gateway seL4 (QEMU + kernel seL4) |
snort/ | IDS Snort 2.9.18 con più profili di regole (vulnerabile a CVE-2022-20685) |
plc/ | Simulazione di teleriscaldamento (libmodbus 3.1.2 vulnerabile, modalità CVE-2022-0367) |
cve_tools/ | Strumenti d'attacco per le dimostrazioni CVE |
scripts/ | Script di utilità e sperimentazione |
docs/ | Documentazione con diagrammi Mermaid (Rete, Container, CVE) |
| Strumento | Bersaglio | Descrizione |
|---|---|---|
cve_14462_attack | libmodbus | Heap buffer overflow tramite disallineamento della lunghezza MBAP |
cve_0367_attack | libmodbus | Heap underflow tramite indirizzo di scrittura non valido (FC 0x17) |
cve_20685_attack | Snort | Ciclo infinito del preprocessore Modbus (DoS) |
cve-2024-1086/ | Kernel Linux | Privilege escalation tramite nf_tables (fuga dal container) |
Il PLC simula un controllore di teleriscaldamento con supporto multi-master (architettura thread-per-client, tipica dei PLC moderni).
| Modalità | Servizio | Porta | Descrizione |
|---|---|---|---|
| CVE-2022-0367 | plc (predefinito) | 5020 | Build ASAN, registri all'indirizzo 100-109 |
| CVE-2019-14462 | plc-14462 | 5022 | Build normale, registri all'indirizzo 0-9 |
# Default mode (CVE-2022-0367 with ASAN)
sudo docker compose up plc
# CVE-2019-14462 mode
sudo docker compose --profile cve14462 up plc-14462
Nella modalità CVE-2022-0367, i registri si trovano agli indirizzi 100-109 (usa l'indirizzo 40101 negli strumenti SCADA):
| Indirizzo | Registro | Descrizione | R/W |
|---|---|---|---|
| 100 | HR[0] | Temperatura interna (°C ÷10) | R |
| 101 | HR[1] | Comando valvola (0-100%) | R/W |
| 102 | HR[2] | Setpoint di temperatura (°C ÷10) | R/W |
| 103 | HR[3] | Modalità (0=Manuale, 1=Auto) | R/W |
| 104 | HR[4] | Temperatura esterna (°C ÷10) | R |
| 105-109 | HR[5-9] | Stato, posizione, temperatura di mandata, tempo di funzionamento, potenza | R |
Questo progetto contiene codice volutamente vulnerabile per la ricerca difensiva sulla sicurezza. Le versioni vulnerabili di libmodbus 3.1.2 e Snort 2.9.18 sono incluse per dimostrare concetti di sicurezza.
Non distribuire componenti non protetti in ambienti di produzione.
Solo per ricerca difensiva autorizzata sulla sicurezza e scopi educativi.