A modular, offline-safe OT/ICS passive network scanner that identifies industrial devices (PLCs, RTUs, FRTUs, IEDs, HMIs, gateways) and detects security vulnerabilities by analysing captured PCAP / PCAPNG traffic. No packets are ever sent to the network, making this safe to run in live OT environments where active scanning can trigger protection relays or disrupt SCADA control.
The repository contains three scanner variants: a unified v2.0 scanner (primary) and two legacy single-purpose scanners (PLC and RTU) that are superseded by v2.0.
Repository: https://github.com/Krishcalin/OT-Security
License: MIT
Python: 3.8+
Dependencies: scapy >= 2.5.0 | dpkt >= 1.9.8 (one required), colorama >= 0.4.6 (optional)
| Scanner | Type | Version | Lines | Target |
|---|---|---|---|---|
ot_scanner/ot_scanner.py |
Unified OT | 2.0.0 | ~22,700 (package) | 16 ICS/SCADA protocols, 9 analysis engines, 11 export formats |
plc_passive_scanner/plc_scanner.py |
PLC-only | 1.0 | 227 | 7 PLC protocols |
rtu_passive_scanner/rtu_scanner.py |
RTU/IED-only | 1.0 | 332 | 9 RTU/IED protocols |
ot_scanner/
├── ot_scanner.py # CLI entry point (~750 lines)
├── scanner/
│ ├── core.py # PCAPAnalyzer — dual scapy/dpkt backend (~1,100 lines)
│ ├── models.py # 20+ data types (dataclasses) (~900 lines)
│ ├── protocols/
│ │ ├── base.py # BaseProtocolAnalyzer + BaseL2Analyzer
│ │ ├── modbus.py # Modbus/TCP (TCP/502)
│ │ ├── s7comm.py # S7comm / S7comm+ with SZL 0x0011/0x001C parsing
│ │ ├── enip.py # EtherNet/IP + CIP
│ │ ├── dnp3.py # DNP3 — stateful sessions + Group 0 attrs
│ │ ├── fins.py # Omron FINS
│ │ ├── melsec.py # MELSEC MC 3E/4E
│ │ ├── iec104.py # IEC 60870-5-104 — stateful
│ │ ├── goose.py # GOOSE + SV (L2)
│ │ ├── iec61850_mms.py # IEC 61850 MMS
│ │ ├── sel_protocol.py # SEL Fast Message
│ │ ├── opcua.py # OPC-UA Binary
│ │ ├── bacnet.py # BACnet/IP
│ │ ├── mqtt.py # MQTT v3.1.1 / v5.0
│ │ ├── profinet.py # PROFINET DCP (L2) + RT (UDP)
│ │ ├── it_detect.py # IT protocol detector (36+ protocols incl. VPN)
│ │ └── behavior.py # Protocol DPI behavior tracker
│ ├── fingerprint/
│ │ ├── engine.py # 7-step vendor fingerprint pipeline
│ │ └── oui_db.py # 144 OUI entries
│ ├── vuln/
│ │ ├── engine.py # VulnerabilityEngine orchestrator + risk scoring
│ │ ├── dnp3_checks.py # 7 DNP3 rules
│ │ ├── iec104_checks.py # 5 IEC-104 rules
│ │ ├── iec61850_checks.py # 6 IEC 61850 rules
│ │ └── general_checks.py # 12 cross-protocol + 5 IT/OT convergence rules
│ ├── risk/
│ │ └── engine.py # CompositeRiskEngine — multi-factor 0-100 scoring
│ ├── threat/
│ │ ├── engine.py # ThreatDetectionEngine — 4 detection modules
│ │ └── signatures.py # 9 ICS malware behavioral signatures
│ ├── attack/
│ │ └── engine.py # AttackPathEngine — BFS pathfinding + kill chain
│ ├── access/
│ │ └── engine.py # SecureAccessEngine — CIP-005 R2 compliance
│ ├── config/
│ │ └── engine.py # ConfigSnapshotEngine — drift detection + LKG baselines
│ ├── policy/
│ │ ├── engine.py # PolicyEngine — 6-stage rule generation
│ │ └── exporters.py # Palo Alto XML, Fortinet CLI, Cisco ACL, JSON
│ ├── project_files/
│ │ ├── engine.py # ProjectFileEngine — directory walker + dispatch
│ │ └── parsers.py # TIA Portal, Rockwell L5X, Schneider XEF, CSV, JSON
│ ├── topology/
│ │ └── engine.py # Purdue zones, zone violations, GraphML export
│ ├── cvedb/
│ │ ├── ics_cves.py # 90 ICS CVEs with EPSS + CISA KEV + exploit maturity
│ │ └── matcher.py # CVEMatcher with Now/Next/Never + KEV/EPSS boost
│ ├── export/
│ │ ├── siem.py # CEF + LEEF syslog export
│ │ ├── stix.py # STIX 2.1 JSON bundle
│ │ ├── servicenow.py # ServiceNow CMDB Import Set JSON
│ │ ├── splunk.py # Splunk HEC NDJSON events
│ │ ├── elastic.py # Elastic Common Schema NDJSON
│ │ └── webhook.py # Webhook notification payload
│ ├── compliance/
│ │ └── engine.py # 35 controls (NERC CIP + IEC 62443 + NIST 800-82)
│ ├── delta/
│ │ └── engine.py # Baseline diff analysis
│ └── report/
│ └── generator.py # JSON, CSV, HTML, GraphML reports
├── tests/ # 57 unit tests (pytest)
│ ├── conftest.py # Shared fixtures (mock devices, zones, CVEs)
│ ├── test_models.py # Dataclass validation
│ ├── test_risk_engine.py # Composite scoring
│ ├── test_threat_engine.py # 9 malware signatures
│ ├── test_attack_engine.py # Attack path analysis
│ ├── test_access_engine.py # Secure access audit
│ ├── test_config_engine.py # Config snapshots + drift
│ ├── test_policy_engine.py # Firewall rule generation
│ ├── test_cve_matcher.py # CVE matching pipeline
│ └── test_exporters.py # Integration exporters
├── pytest.ini # Test configuration
└── requirements-dev.txt # pytest>=7.0
PCAP File
│
├── scapy / dpkt packet reader (core.py)
│ ├── Layer-2 frames → L2 analyzers (GOOSE, SV, PROFINET DCP)
│ └── IP/TCP/UDP packets → IP analyzers (Modbus, S7comm, ENIP, ...)
│ └── IT protocol detector → ITProtocolHit accumulation
│
├── Per-device OTDevice registry (IP → OTDevice)
├── Communication flow table ((src, dst, proto, port) → CommFlow)
│
└── Finalisation pipeline (core._finalise):
1. Collect stateful sessions (DNP3, IEC-104, GOOSE)
2. GOOSE publisher → IP device linking
3. Merge project file ground-truth devices
4. Vendor fingerprinting (7-step pipeline, skip ground_truth)
5. Protocol behavior analysis (DPI stats)
6. IT protocol attachment
7. Asset criticality inference (safety/process/monitoring/support)
8. Communication profile computation (master/slave/peer)
9. Device filtering (min_packets threshold + ground_truth)
10. Vulnerability assessment (4 check modules)
11. CVE matching (Now/Next/Never with EPSS/KEV boost)
12. Topology analysis (Purdue zones, violations, edges)
13. Composite risk scoring (0-100 multi-factor)
14. Threat detection (malware sigs, anomalies, recon, unauthorized cmds)
15. Secure access audit (VPN/RDP/SSH, CIP-005 compliance)
│
└── Return: (devices, flows, zones, violations, edges)
│
├── Console summary (with threat/attack/remote access alerts)
├── Attack path analysis (multi-hop BFS, kill chain, remediation)
├── Configuration snapshots (persistent store, drift detection)
├── JSON / CSV / HTML / GraphML reports
├── CEF / LEEF / STIX / ServiceNow / Splunk / Elastic / Webhook exports
├── Firewall policy generation (Palo Alto, Fortinet, Cisco, JSON)
├── Compliance report (NERC CIP + IEC 62443 + NIST 800-82)
└── Delta analysis (compare against baseline)
20+ dataclasses model the scanner's domain:
| Type | Description |
|---|---|
ProtocolDetection |
Detected industrial protocol on a device |
VulnerabilityFinding |
Security vulnerability with MITRE ATT&CK mapping |
CommFlow |
Directional communication flow between devices |
NetworkZone |
/24 subnet mapped to Purdue level (0-5) |
ZoneViolation |
Cross-zone communication breach |
CVEEntry |
Known ICS CVE with EPSS, CISA KEV, exploit maturity |
CVEMatch |
CVE matched to device with Now/Next/Never priority |
ProtocolStats |
DPI statistics per protocol (function codes, read/write/control) |
ITProtocolHit |
IT/enterprise protocol detected on OT network |
TopologyEdge |
Directed edge in network topology graph |
DNP3SessionState |
Per-session DNP3 state (SA, commands, file transfers) |
IEC104SessionState |
Per-session IEC-104 state (commands, clock syncs) |
GOOSEPublisherState |
Per-publisher GOOSE state (simulation, TTL, confRev) |
PolicyRule |
Firewall rule recommendation with compliance refs |
PolicyRuleSet |
Complete rule collection organized by zone |
ThreatAlert |
Threat detection alert with MITRE technique/tactic |
RemoteAccessSession |
Remote access session with CIP-005 compliance status |
DeviceConfig |
Point-in-time device configuration snapshot |
ConfigDriftAlert |
Configuration change alert with MITRE mapping |
AttackPath |
Multi-hop attack path with score and kill chain |
OTDevice |
Unified device model combining all fields |
| Engine | Module | Purpose |
|---|---|---|
| CompositeRiskEngine | risk/engine.py |
Multi-factor 0-100 risk scoring (CVSS, EPSS, KEV, criticality, exposure) |
| ThreatDetectionEngine | threat/engine.py |
9 ICS malware signatures, anomaly baselines, recon detection |
| AttackPathEngine | attack/engine.py |
BFS pathfinding, crown jewel identification, kill chain mapping |
| SecureAccessEngine | access/engine.py |
Remote access audit, jump server detection, CIP-005 compliance |
| ConfigSnapshotEngine | config/engine.py |
Persistent snapshots, drift detection, LKG baselines |
| PolicyEngine | policy/engine.py |
6-stage firewall rule generation, 4 export formats |
| ProjectFileEngine | project_files/engine.py |
ICS project file parsing (TIA Portal, L5X, XEF, CSV, JSON) |
| TopologyEngine | topology/engine.py |
Purdue zones, violation detection, GraphML |
| VulnerabilityEngine | vuln/engine.py |
29 behavioral rules, risk scoring, role inference |
| Malware | Year | Pattern | MITRE |
|---|---|---|---|
| Industroyer/CrashOverride | 2016 | IEC-104 control + GI + clock sync | T0855, T0831 |
| TRITON/TRISIS | 2017 | SIS program download + firmware update | T0839, T0836 |
| Havex | 2014 | OPC-UA high peers + diagnostics | T0846 |
| BlackEnergy | 2015 | Multi-protocol + IT + program upload | T0869, T0859 |
| Pipedream/Incontroller | 2022 | S7comm download + Modbus writes | T0836, T0855 |
| Stuxnet | 2010 | S7comm upload + download (different sources) | T0843, T0845 |
| FrostyGoop | 2024 | Modbus writes from higher Purdue zone | T0855 |
| Fuxnet | 2024 | Modbus flood writes + diagnostics (PLC bricking) | T0831 |
| IOControl | 2024 | MQTT C2 + IT protocols on IoT gateways | T0869 |
T0816, T0831, T0836, T0839, T0842, T0843, T0845, T0846, T0855, T0858, T0859, T0869, T0882, T0886
python ot_scanner.py PCAP_FILE [options]
Individual reports:
--json FILE, --csv FILE, --html FILE, --graphml FILE
CVE database:
--cve-db FILE Load additional CVE entries from JSON
SIEM export:
--cef FILE CEF syslog (Splunk / ArcSight)
--leef FILE LEEF syslog (QRadar)
--stix FILE STIX 2.1 JSON bundle
Platform integrations:
--servicenow FILE ServiceNow CMDB import JSON
--splunk-hec FILE Splunk HEC NDJSON events
--elastic-ecs FILE Elastic Common Schema NDJSON
--webhook FILE Webhook notification payload
Compliance & delta:
--compliance FILE NERC CIP + IEC 62443 + NIST 800-82
--delta FILE Compare against baseline JSON
Configuration snapshots:
--snapshot-dir DIR Persistent configuration snapshot directory
--set-baseline Mark current scan as "last known good"
Firewall policy:
--policy DIR Generate rules (Palo Alto, Fortinet, Cisco, JSON)
Project files:
--project-dir DIR ICS project files for ground-truth enrichment
Directory output:
-o DIR, --output DIR Output directory (auto-names)
-f {json,csv,html,all} Report format (default: all)
Filtering & analysis:
--severity {critical,high,medium,low}
-v, --verbose Per-packet detections
--min-packets N Minimum packets per device (default: 2)
--version
Exit code: 1 if CRITICAL or HIGH findings, 0 otherwise.
| Prefix | Module | Count |
|---|---|---|
RTU-DNP3-NNN |
vuln/dnp3_checks.py |
7 |
RTU-104-NNN |
vuln/iec104_checks.py |
5 |
RTU-61850-NNN |
vuln/iec61850_checks.py |
6 |
OT-GEN-NNN |
vuln/general_checks.py |
4 |
OT-OPCUA-NNN |
vuln/general_checks.py |
1 |
OT-MQTT-NNN |
vuln/general_checks.py |
2 |
OT-ITOT-NNN |
vuln/general_checks.py |
5 |
ZV-NNN |
topology/engine.py |
5 |
PR-NNN |
policy/engine.py |
dynamic |
TA-NNN |
threat/engine.py |
dynamic |
RA-NNN |
access/engine.py |
dynamic |
CD-NNN |
config/engine.py |
dynamic |
AP-NNN |
attack/engine.py |
dynamic |
- Create file in
scanner/protocols/, subclassBaseProtocolAnalyzerorBaseL2Analyzer - Implement
can_analyze()andanalyze()returning[(ip, ProtocolDetection)] - Register in
scanner/core.py(_ip_analyzersor_OPTIONAL_IP_ANALYZERS) - Add port(s) to
INDUSTRIAL_PORTSincore.py
- Add check function to appropriate module (
vuln/dnp3_checks.py, etc.) - Follow ID pattern:
{PREFIX}-{NNN}(e.g.,RTU-DNP3-008) - Return
VulnerabilityFindingwith all fields includingmitre_attack - Wire into parent
run_*_checks()dispatcher
- Add dict to
ICS_CVE_DATABASEincvedb/ics_cves.py - Include:
cve_id,vendor,product_pattern(regex),affected_versions,severity,cvss_score,has_public_exploit,epss_score,is_cisa_kev,exploit_maturity
- Python 3.8+ stdlib only (except scapy/dpkt + colorama)
- All sub-engines loaded via
try/except ImportErrorfor graceful degradation dataclassesfor all model types withto_dict()methods- HTML reports use Catppuccin Mocha dark theme
- Exit code 1 on CRITICAL/HIGH for CI/CD gating
- Deterministic STIX UUIDs (uuid5)
57 unit tests across 9 test files covering all analysis engines. Tests use mock data only — no PCAP files required.
cd ot_scanner
pip install -r requirements-dev.txt
python -m pytest tests/ -v| Test File | Tests | Coverage |
|---|---|---|
test_models.py |
9 | Dataclass to_dict(), field defaults |
test_risk_engine.py |
5 | Composite scoring, multipliers, compensating controls |
test_threat_engine.py |
7 | All 9 malware signatures + unauthorized command alerts |
test_attack_engine.py |
6 | BFS pathfinding, crown jewels, path scoring, kill chain |
test_access_engine.py |
4 | CIP-005 compliance, jump server detection |
test_config_engine.py |
8 | Snapshot capture/save/load/diff, baseline, drift detection |
test_policy_engine.py |
5 | Rule generation, priority ordering, safety isolation |
test_cve_matcher.py |
9 | 90 CVEs loaded, EPSS/KEV propagation, matching pipeline |
test_exporters.py |
4 | ServiceNow, Splunk HEC, Elastic ECS, Webhook payloads |
CI Pipeline: GitHub Actions (.github/workflows/ci.yml) runs on every push/PR against Python 3.8, 3.10, and 3.12.
7 protocols, vendor fingerprinting, basic risk scoring. Superseded by v2.0.
9 protocols, 21 vulnerability rules, GOOSE/MMS session tracking. Superseded by v2.0.