ICS/OT cybersecurity scanner — Modbus, S7Comm & DNP3. Passive PCAP analysis + safe read-only Modbus scans. JSON/HTML reports + executive dashboards.
IndustrialScanner
Read-only security analyzer for Industrial Control Systems (ICS) / Operational Technology (OT)
Modbus/TCP · Siemens S7Comm · DNP3
⚠️ Read-only research / education tool. Never deploy against production OT environments without explicit written authorization. See SECURITY.md and the Safe Harbor section.
What it does
IndustrialScanner gives OT/ICS security practitioners a safe, read-only, automated analysis suite for the three most common industrial protocols:
| Module | Protocol | Mode | Output | |---|---|---|---| | modbus_scanner | Modbus/TCP (port 502) | Active read-only probe (coils, inputs, registers) | JSON + HTML | | s7commanalyzer | Siemens S7Comm (TPKT/COTP, port 102) | Passive PCAP analysis | JSON + HTML | | dnp3_monitor | DNP3 (TCP/UDP 20000) | Passive PCAP analysis | JSON + HTML | | build*index.py | All three | Consolidated dashboard | HTML + Chart.js |
The suite is intentionally split into report generation (scanners/analyzers) and dashboard building (index builders). Scanners produce per-target reports; index builders aggregate them into executive dashboards.
Why it exists
ICS/OT networks are not regular IT networks. They prioritize availability and safety over speed and convenience, and they use specialized protocols that traditional security tooling ignores. A single misconfiguration can halt a substation, a production line, or a water treatment plant.
Commercial ICS/OT tooling (Claroty, Nozomi, Dragos) is excellent but expensive and closed-source. IndustrialScanner closes that gap by giving practitioners, researchers, and educators a transparent, auditable, and free toolkit to understand the security posture of their OT assets.
Quickstart
# 1. Clone
git clone https://github.com/frangelbarrera/industrial-scanner.git
cd IndustrialScanner
2. Install
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
3. Copy env template (optional for read-only analysis)
cp .env.example .env
4. Run a Modbus scan (read-only, safe probes)
industrial-scanner modbus --targets 127.0.0.1 --unit 1
or
python -m modbusscanner.modbusscan --targets 127.0.0.1 --unit 1
5. Analyze S7Comm PCAPs (single file)
industrial-scanner s7 --pcap pcaps/s7/step7s300stop.pcapng
6. Batch-analyze all DNP3 PCAPs in pcaps/dnp3/
python rundnp3all.py
7. Build consolidated dashboards
python builds7index.py # S7 dashboard
python builddnp3index.py # DNP3 dashboard
python buildglobalindex.py # Global executive dashboard
Outputs land in reports/ as HTML dashboards with Chart.js visualizations.
Repository layout
IndustrialScanner/
├─ modbus_scanner/ # Active read-only Modbus/TCP scanner
│ ├─ modbus_scan.py
│ └─ utils.py
├─ s7commanalyzer/ # Passive S7Comm analyzer
│ ├─ s7_analyze.py
│ └─ parsers.py
├─ dnp3_monitor/ # Passive DNP3 analyzer
│ ├─ dnp3_analyze.py
│ └─ parsers.py
├─ ics_scanner/ # Shared security primitives (NEW)
│ ├─ security.py # HTML escape, target policy, path guards
│ └─ cli.py # Unified Click+Rich CLI
├─ reports/
│ ├─ modbus_batch/
│ ├─ s7_batch/
│ ├─ dnp3_batch/
│ ├─ templates/ # Jinja2 templates (autoescape on)
│ ├─ modbus_index.html
│ ├─ s7_index.html
│ ├─ dnp3_index.html
│ └─ index.html
├─ tests/ # pytest + property-based tests (NEW)
├─ .github/workflows/ # CI, release, dependency scan (NEW)
├─ pcaps/ # Sample PCAPs
├─ docs/images/ # Screenshots
├─ cli.py # Legacy CLI (kept for compatibility)
├─ build_*.py # Dashboard builders
├─ pyproject.toml # Modern packaging (PEP 621)
├─ .pre-commit-config.yaml # Ruff + mypy + bandit hooks
└─ .env.example # Environment template
Unified CLI (new)
The new Click+Rich based CLI lives in ics_scanner/cli.py. Install exposes the industrial-scanner entry point:
industrial-scanner modbus --targets 192.168.0.10,192.168.0.11 --unit 1
industrial-scanner s7 --pcap pcaps/s7/step7s300stop.pcapng
industrial-scanner dnp3 --pcap pcaps/dnp3/readandresponse.pcap
The CLI enforces a target safety policy: public IPs are refused by default (use --allow-public only after explicit written authorization). This prevents accidental wide-area scanning of legacy PLCs.
Usage by protocol
Modbus (active, read-only)
python -m modbusscanner.modbusscan --targets 127.0.0.1 --port 502 --unit 1
or
industrial-scanner modbus --targets 127.0.0.1 --port 502 --unit 1
- Issues only read function codes:
0x01 Read Coils,0x02 Read Discrete Inputs,0x03 Read Holding Registers,0x04 Read Input Registers. - Collects latency, exposure signals (
unauthenticatedread,broadregister_access). - Outputs JSON + HTML in
reports/modbus_batch/.
S7Comm (passive, from PCAPs)
industrial-scanner s7 --pcap pcaps/s7/step7s300stop.pcapng
python -m s7commanalyzer.s7_analyze # batch-process all PCAPs in pcaps/s7/
python builds7index.py # consolidated dashboard with Chart.js
- Unwraps TPKT/COTP framing (RFC 1006 / ISO 8073) before parsing the S7 PDU.
- Strict binary parser per Siemens spec: decodes the S7 header (ROSCTR) and parameter block (function code at offset 10), not heuristic ASCII matching.
- Function classifier:
ReadVar,WriteVar,Start,Stop,DownloadBlock,CopyRamToRom,FirmwareUpdate,Password,ReadDiag. - Each packet is enriched with MITRE ATT&CK for ICS techniques (T0801, T0802, T0808, T0848, T0858, T0859, T0879, T0885, T0881).
DNP3 (passive, from PCAPs)
industrial-scanner dnp3 --pcap pcaps/dnp3/readandresponse.pcap
python rundnp3all.py # batch-process all PCAPs in pcaps/dnp3/
python builddnp3index.py
- Strict binary parser per IEEE 1815-2012: decodes the link layer (10 bytes), transport layer, and application layer (function code at offset 12).
- Function classifier:
Read,Write,Select,Operate,DirectOperate,ColdRestart,WarmRestart,StopApplication,DeleteFile,EnableUnsolicited,AssignClass,Authenticate, and 16 more. - Each suspect function is flagged and enriched with MITRE ATT&CK for ICS techniques.
Global executive dashboard
python buildglobalindex.py
Produces reports/index.html with totals and quick links per protocol.
Screenshots
Demo (animated)

The demo cycles through the global executive dashboard, S7Comm dashboard, DNP3 dashboard, and a per-PCAP DNP3 report showing MITRE ATT&CK mapping in action.
Global Executive Dashboard

S7Comm Dashboard (with Chart.js visualizations)

DNP3 Dashboard (with Chart.js visualizations)

Per-PCAP report with MITRE ATT&CK enrichment
S7 individual report — shows parsed packets with ROSCTR, function code, and hints: 
DNP3 individual report — shows parsed packets with function code, suspect flag, and MITRE techniques: 
Test data
The repo bundles:
- Sample PCAPs for S7Comm (
.pcapng) and DNP3 (.pcap) underpcaps/. - ModbusPal.jar, a third-party Modbus/TCP emulator, for spinning up a local test target. (External dependency; report issues to its upstream project.)
Development
pip install -e ".[dev]"
pre-commit install
pytest
ruff check .
mypy modbusscanner s7commanalyzer dnp3monitor ics_scanner
CI/CD
The repo ships three GitHub Actions workflows under .github/workflows/:
| Workflow | Purpose | |---|---| | ci.yml | Lint (ruff), type-check (mypy), tests (3.11/3.12/3.13), security-scan (bandit + pip-audit + semgrep + CodeQL), build | | release.yml | On tag v*, publish to PyPI + GitHub Release | | dependency-scan.yml | Daily pip-audit of pinned dependencies |
Conventions
- Style: Ruff (line-length 100, pyupgrade, bugbear, simplify, security).
- Types: strict mypy on
modbusscanner,s7commanalyzer,dnp3monitor,ics_scanner. - Tests: pytest + hypothesis for fuzz testing of parsers.
- Commits: conventional-commits style (
feat:,fix:,sec:,docs:,ci:). - Versioning: Semantic Versioning 2.0.0.
Security
See SECURITY.md for the full policy, supported versions, vulnerability reporting flow, Safe Harbor, and known security considerations.
Key highlights:
- Read-only by design: Modbus scanner issues only
0x01–0x04function codes. - Target safety policy: public IPs are refused by default (
--allow-publicrequires explicit opt-in). - HTML/JSON output escaping: all untrusted PCAP bytes are escaped via
icsscanner.security.htmlescape()or Jinja2autoescape=True. - No write/control primitives for S7Comm or DNP3.
- No telemetry / no outbound calls: the tool runs entirely offline.
Compliance references
IndustrialScanner is positioned against the following standards (see docs/compliance.md for the full gap analysis):
- IEC 62443 (industrial automation and control systems security)
- NIST SP 800-82 Rev 3 (Guide to Operational Technology (OT) Security)
- NERC CIP (Critical Infrastructure Protection)
- ISO/IEC 27019 (Energy utility industry security)
Roadmap
| Quarter | Theme | Highlights | |---|---|---| | Q1 | Stabilization | Strict parsers (per IEEE 1815 / IEC 61131), type hints everywhere, 80%+ coverage | | Q2 | Security hardening | DNP3 SA v5, Modbus/TCP TLS (RFC 9441), SBOM generation, signed releases | | Q3 | Architecture | Plugin system for new protocols (PROFINET, EtherNet/IP, IEC 61850), async I/O | | Q4 | Ecosystem | PyPI release, Docker image, MkDocs site, community Discord, S4 / Black Hat ICS talks |
A note on language composition
GitHub's language breakdown shows a high HTML percentage because the tool generates HTML dashboards. The actual application logic is entirely Python — see modbusscanner/, s7commanalyzer/, dnp3monitor/, and ics_scanner/.
License
MIT — see LICENSE.
Contributing
PRs welcome. Please read SECURITY.md first, run pre-commit install, and ensure all CI checks pass before requesting review.
For larger changes, open an issue first to discuss the design.
Acknowledgements
Built on the shoulders of giants:
- scapy — packet manipulation
- pymodbus — Modbus implementation
- Jinja2 — HTML templating
- ModbusPal — Modbus emulator
- snap7 — Siemens S7 reference
- The broader ICS-CERT, SANS ICS, and Dragos research communities
frangelbarrera.