A free, open-source ICS/SCADA cybersecurity training platform for researchers, educators, and students. Build realistic industrial control system environments on your laptop — no hardware, no subscription, no cost.
Developed by Ian Burres, Professor of Practice at the University of Texas at San Antonio (UTSA), in support of ICS/SCADA security education and research.
Drag-and-drop SCADA canvas — OT layer with PLC, sensors, actuators, and live Modbus protocol edges
Full Kali Linux Xfce4 desktop in a dedicated window — ICS attack toolkit, Wireshark, and Metasploit pre-installed
Live Suricata IDS alerts and Zeek deep-packet logs during an active Modbus coil-write attack
Guided tutorial overlay — step-by-step instructions with success checks, copy-to-clipboard commands, and attack context
Engineering workstation Xfce4 desktop — Wireshark, ICS protocol scripts, and one-click shortcuts to OpenPLC and FUXA HMI
OTForge lets you design, deploy, and attack realistic ICS/SCADA environments using a visual drag-and-drop canvas. Each device in your scenario runs as a real Docker container on an isolated virtual network — protocol traffic is genuine, not simulated at the application layer.
Author a scenario → drag PLCs, RTUs, IEDs, sensors, and network devices onto the canvas, wire them with protocol edges, write IEC 61131-3 ladder logic, configure the firewall — then click Run. Docker Compose spins up the full environment in seconds.
Attack the scenario → launch the Kali Linux desktop (Wireshark, Metasploit, ICS-specific tools) in a dedicated OS window via KasmVNC and work through the mission.
Monitor and analyze → live Grafana dashboards show Suricata IPS alerts and Zeek protocol logs alongside the InfluxDB process historian.
- Drag-and-drop SCADA canvas with ISA-5.1 / IEC 81346 standard P&ID symbols
- Six-zone Purdue Reference Model network topology (IEC 62443-3-2 / NIST SP 800-82):
- OT (L0–L2) — PLCs, RTUs, IEDs, sensors, actuators, field devices
- Control Center (L3) — HMI, historian, engineering workstation, application/database servers
- Plant DMZ (L3.5) — Firewall, IDS/IPS, router, switch
- Enterprise (L4) — Domain controller, web/business servers, enterprise desktops
- Internet DMZ (L5) — Email servers, internet-facing servers
- Red Team — Kali Linux attack machine (isolated attacker network)
- Zone-aware firewall rule editor with nftables enforcement
- Self-healing IP assignment — the compose generator automatically resolves duplicate IPs on every simulation start
- Delete Scenario button — clears all devices with a confirmation prompt
- Export scenarios as
.otflabfiles — share with students or the community - Network Settings modal — configure Docker subnet addresses per zone
- Structured Text (ST) editor with syntax highlighting
- SVG ladder logic viewer
- Live deploy to running OpenPLC Runtime containers via the web API
- IEC 61131-3 compliant (Ladder, ST, FBD, SFC, IL)
- Full Kali Linux Xfce4 desktop via KasmVNC — opens in a dedicated OS window
- Moveable to a second monitor for a realistic red team / blue team split-screen setup
- Complete ICS-focused attack toolkit (see below)
- One-click launch from the toolbar when the simulation is running
- Suricata IPS with Emerging Threats ICS ruleset (Modbus, DNP3, EIP anomaly detection)
- Zeek deep-packet analysis with ICS protocol scripts
- Grafana dashboards for real-time alert visualization
- InfluxDB 1.8 process historian
Real protocol packets flow on Docker virtual networks — scanner tools and exploit frameworks see genuine service fingerprints.
| Protocol | Implementation | Port |
|---|---|---|
| Modbus TCP / RTU / ASCII | pymodbus (Python) | TCP 502 |
| DNP3 | OpenDNP3 (C++) | TCP 20000 |
| OPC UA | node-opcua (Node.js) | TCP 4840 |
| BACnet/IP | bacpypes (Python) | UDP 47808 |
| Ethernet/IP CIP | cpppo (Python) | TCP 44818 |
| IEC 61850 | libiec61850 (C) | TCP 102 |
| S7comm (Siemens S7) | Pure Python (RFC 1006 / COTP / S7 PDU) | TCP 102 |
| IEC 60870-5-104 | Pure Python (APCI / ASDU) | TCP 2404 |
| Modbus TCP (process sim) | pymodbus 3.7 + physics loop (water tank / pipeline / generator) | TCP 502 |
| HTTP (company site) | nginx 1.27 — Meridian Process Controls OSINT target | TCP 80 |
| DNS | dnsmasq — authoritative resolver for meridian-process.com | UDP/TCP 53 |
| Category | Tools |
|---|---|
| Reconnaissance | nmap, masscan, netdiscover, arp-scan |
| Packet analysis | Wireshark (GUI), tshark, tcpdump, Scapy |
| Exploitation | Metasploit Framework, Armitage (GUI) |
| Credentials | Hydra, Medusa, Patator, John the Ripper, Hashcat |
| ICS/OT specific | pymodbus, dnp3-python, opcua, bacpypes3, python-snap7 (S7comm), impacket, ike-scan |
| Desktop | Full Kali Xfce4 via noVNC (port 6080) — launches in a dedicated OS window |
| Requirement | Minimum |
|---|---|
| OS | Windows 10 22H2+ or Windows 11 23H2+, macOS 13+, Ubuntu 22.04+ |
| RAM | 8 GB (16 GB recommended for attack machine scenarios) |
| Disk | 20 GB free (Docker images downloaded on first run) |
| Docker Desktop | Latest stable |
| Node.js | 22+ (development only — Node 20 is EOL and incompatible with Vite 8) |
git clone https://github.com/iburres/otforge.git
cd otforge
npm ci
npm run build:packages
npm run devDocker Desktop must be running before launching the app.
Node.js 22+ required. Vite 8 uses
crypto.hash(), which was added in Node 21.7. Node 20 will throwTypeError: crypto.hash is not a function.
First-time students: see docs/student-setup.md (also available as docs/student-setup.html) for a full step-by-step walkthrough covering Docker, Git, Node.js, and OTForge setup on both Windows and macOS.
# Windows
npm run build:win
# macOS
npm run build:mac
# Linux
npm run build:linuxotforge/
├── packages/
│ ├── app/ # Electron application
│ │ └── src/
│ │ ├── main/ # Node.js main process (IPC, Docker, OpenPLC API)
│ │ ├── preload/ # contextBridge API surface
│ │ └── renderer/ # React + TypeScript UI
│ │ └── src/
│ │ ├── canvas/ # React Flow SCADA canvas + layer tabs
│ │ ├── palette/ # Device palette (ISA-5.1 symbols)
│ │ ├── properties/ # Device inspector + PLC IDE panel
│ │ ├── terminal/ # Attack terminal modal (KasmVNC)
│ │ ├── monitor/ # Grafana + Loki monitor panel
│ │ ├── settings/ # Network subnet settings modal
│ │ ├── tutorial/ # TutorialPanel guided step-by-step overlay
│ │ └── icons/ # SVG device icons
│ ├── orchestrator/ # Docker Compose generator + DockerClient
│ └── schema/ # Shared TypeScript types (OTForgeScenario, DeviceConfig…)
├── containers/ # Docker image source (one per device category)
│ ├── openplc/ # OpenPLC Runtime
│ ├── suricata/ # Suricata IPS with ICS rules
│ ├── zeek/ # Zeek network monitor
│ └── firewall/ # nftables firewall
├── scenarios/ # Bundled .otflab scenario files
│ └── tutorial-01-modbus-coil-write.otflab # Tutorial 01: Modbus Coil Write
└── .github/
└── workflows/ # CI: build, Docker image publish, CodeQL, secret scan
Each scenario runs six isolated Docker bridge networks matching the Purdue Reference Model:
| Zone | Network | Subnet | Devices |
|---|---|---|---|
| OT (L0–L2) | ot-net |
10.200.10.0/24 | PLC, RTU, IED, sensor, actuator, pump, valve, flow meter, pressure transmitter |
| Control Center (L3) | control-net |
10.200.20.0/24 | HMI, historian, engineering workstation, application server, database server |
| Plant DMZ (L3.5) | plant-dmz-net |
10.200.30.0/24 | Firewall, IDS/IPS, router, switch |
| Enterprise (L4) | enterprise-net |
10.200.40.0/24 | Domain controller, web server, business server, enterprise desktop |
| Internet DMZ (L5) | internet-dmz-net |
10.200.50.0/24 | Email server, internet server |
| Red Team | attacker-net |
10.200.60.0/24 | Kali Linux attack machine |
System services (InfluxDB, Loki, Grafana, FUXA, Promtail) occupy .240–.249 in their respective zone; user devices start at .10 and increment automatically.
Scenarios are saved as .otflab JSON files with four layers:
{
"meta": { "name": "Water Treatment Plant", "sector": "water", "version": "1.0" },
"visual": { "nodes": [...], "edges": [...] },
"network": { "segments": [...], "protocolEdges": [...] },
"devices": { "devices": { "plc-1": { ... }, "rtu-1": { ... } } },
"security": { "firewallRules": [...], "idsConfig": { ... } }
}Share your scenarios with the community — open a pull request against the otforge-scenarios repository (coming soon).
Pack multiple scenarios, custom device types, and sector-specific detection rules into a single .otfpack ZIP:
pack.json — manifest (id, name, version, author, sector, ...)
scenarios/ — pre-built .otflab scenario files
devices/
registry.json — custom device types (label + Docker image override)
icons/ — SVG icons displayed in the palette
rules/
suricata/ — .rules files (Emerging Threats format)
zeek/ — .zeek protocol analysis scripts
Install packs via Toolbar → Packs → Install Pack (Author mode). Installed packs appear in the Pack Manager where you can open bundled scenarios or uninstall packs. Custom device types from packs appear in the palette under Pack Devices, draggable onto the canvas like any built-in device.
| Phase | Feature | Status |
|---|---|---|
| 0 | Electron shell, Docker check, first-launch flow | ✅ Complete |
| 1 | Orchestration engine (Compose generator, LevelDB, resource estimator) | ✅ Complete |
| 2 | SCADA canvas (React Flow, ISA-5.1 icons, zones, drag-drop) | ✅ Complete |
| 3 | Container images (GHCR images, GitHub Actions CI/CD) | ✅ Complete |
| 4 | PLC IDE (ST editor, ladder viewer, variable bindings, live deploy) | ✅ Complete |
| — | Attack terminal → KasmVNC Kali desktop in dedicated OS window | ✅ Complete |
| 5 | DNP3 IED auto-config + security stack UI (FirewallPanel, IDSPanel, nftables/Suricata/Zeek) | ✅ Complete |
| — | Connection validation — Purdue Reference Model matrix (IEC 62443-3-2 / NIST SP 800-82) | ✅ Complete |
| 6 | Monitoring panels — Grafana ICS Lab Overview + native Loki log viewer + Promtail sidecar | ✅ Complete |
| — | Six-zone Purdue Model restructure (OT / Control / Plant DMZ / Enterprise / Internet DMZ / Attacker) | ✅ Complete |
| — | Self-healing IP deduplication in compose generator | ✅ Complete |
| — | Network Settings modal — per-zone subnet configuration | ✅ Complete |
| — | Delete Scenario button with confirmation | ✅ Complete |
| 7 | FUXA HMI embed + PLC → HMI Modbus wiring | ✅ Complete |
| 8 | Author / Student mode split + locked scenario distribution | ✅ Complete |
| 9 | Community scenario pack format (.otfpack ZIP — Pack Manager, custom device types, bundled Suricata/Zeek rules) | ✅ Complete |
| 10 | Conpot legacy device emulation (Siemens S7, IEC 104) | ✅ Complete |
| 11 | Physical process simulation (water tank, pipeline, generator dynamics) | ✅ Complete |
| 12 | Attack infrastructure — company website (Meridian Process Controls), DNS server, Kali noVNC desktop | ✅ Complete |
| 13 | Guided tutorial system — TutorialPanel overlay, Tutorial 01 (Modbus Coil Write), DnsConfig schema | ✅ Complete |
| 14 | macOS + Linux packaging and distribution | 🔜 Planned |
This project would not be possible without these open-source tools:
| Component | Role | License |
|---|---|---|
| OpenPLC Runtime | IEC 61131-3 PLC execution engine | GPL-3.0 |
| FUXA | Web-based SCADA/HMI | MIT |
| Suricata | Network IPS / IDS | GPL-2.0 |
| Zeek | Network traffic analysis | BSD-3 |
| Grafana | Dashboards and visualization | AGPL-3.0 |
| Loki | Log aggregation | AGPL-3.0 |
| InfluxDB 1.8 | Time-series process historian | MIT |
| React Flow | SCADA canvas | MIT |
| xterm.js | Terminal emulator | MIT |
| Kali Linux (linuxserver) | Penetration testing OS via KasmVNC | Various |
Third-party Docker images are pulled from public registries at runtime and are not bundled in this repository.
Contributions are welcome — bug reports, new device types, scenario packs, protocol implementations, and documentation improvements are all valuable.
- Fork the repository
- Create a feature branch (
git checkout -b feature/dnp3-master-station) - Commit your changes with clear messages
- Open a pull request
For large changes, open an issue first to discuss the approach.
Application source code: MIT
Runtime Docker images are governed by their own licenses — see the LICENSE file for a full list.
Ian Burres Professor of Practice — Cybersecurity, University of Texas at San Antonio (UTSA) Former: Sandia National Laboratories ORCID: 0009-0006-1320-9956