Most security testing advice assumes that the worst thing you can break is a web application. In operational technology, the worst thing you can break is a process – and processes move pumps, valves, conveyors and turbines.
That difference is why every OT walkthrough on CyberLabs starts in a lab rather than on a live plant network. This article shows you how to build that lab: an isolated, restorable environment where you can capture industrial traffic, validate segmentation, write detection rules and make mistakes without consequence.
You do not need industrial hardware to begin. A laptop with 16 GB of RAM and a hypervisor is enough for a first build. Physical switches, firewalls and controllers can be added later, once the software environment has proven itself.
1. What you will learn
By the end of this build you will have an isolated OT environment capable of supporting:
- Passive asset discovery
- Modbus/TCP analysis
- Firewall and VLAN testing
- OT network scanning experiments
- HMI and PLC simulation
- Windows engineering workstation testing
- Detection-rule development
- Packet capture and evidence collection
- Backup and recovery exercises
Every future CyberLabs OT article will reference this lab, so it is worth building carefully once rather than improvising each time.
2. OT safety classification
Green – Controlled lab activity.
No production industrial systems are involved. All testing takes place inside an isolated virtual environment with no path to a live process, a corporate network or the internet from the OT segments.
CyberLabs classifies every OT article this way so that readers know, before they start, whether an activity is safe to reproduce in a lab, requires a maintenance window, or should never be attempted outside a test environment.
3. Why an OT lab is different
An IT penetration tester works in a world where confidentiality usually ranks first. In OT, the priority order commonly inverts: availability and safety come before confidentiality. That single change has practical consequences.
Availability is the primary control objective. A short outage on a file server is an inconvenience. The same outage on a control network can halt a batch, trip a safety system or force an unplanned shutdown with hours of restart cost behind it.
Ordinary traffic can be disruptive. Embedded controllers often run minimal TCP/IP stacks with small connection tables. Aggressive port scans, broadcast storms and rapid connection cycling have been known to hang devices that would shrug off the same traffic in an IT context. The device does not need a vulnerability to fail – it only needs to run out of resources.
A perfectly valid command can still be dangerous. Modbus/TCP has no authentication. A write to a coil is not an exploit; it is the protocol working exactly as designed. Anything that can reach TCP/502 and speak the protocol can start a pump. This is why “read-only” discipline matters more in OT than the vulnerability count.
Recovery must be planned before testing, not after. In a lab that means snapshots and exported configurations. On a real site it means agreed rollback procedures, known-good backups and someone with authority to stop the test.
Production testing requires explicit, written authorisation. Scope, timing, permitted techniques, escalation contacts and abort criteria should be agreed with operations before anything touches a live network – usually alongside a maintenance window and an operator who can revert to manual control.
The lab exists so that you can learn all of the above without a process engineer standing behind you.
4. Lab components
Everything below is free or has a usable free tier. Use the tools you already know; an unrealistically expensive lab is a lab you will never finish.
| Role | Suggested software |
|---|---|
| Hypervisor | VMware Workstation Pro, VMware Fusion or VirtualBox |
| Security workstation | Kali Linux or Ubuntu with your usual toolset |
| Engineering workstation | Windows 10 or 11 (evaluation image is sufficient) |
| Firewall / segmentation | pfSense or OPNsense as a virtual appliance |
| PLC | OpenPLC Runtime and OpenPLC Editor |
| HMI / SCADA | ScadaBR, FUXA or Rapid SCADA |
| Protocol simulation | ModbusPal, mbpoll, pymodbus or a Modbus client of your choice |
| Traffic analysis | Wireshark |
| Detection | Zeek and/or Suricata |
| Log aggregation (later) | FortiSIEM, Wazuh or Elastic |
Resource guidance. Four VMs running simultaneously – firewall, Windows jump host, OpenPLC and HMI – will run comfortably in 16 GB of RAM if you keep the Linux VMs at 1-2 GB each and give Windows 4 GB. The monitoring VM can be started only when you need it.
5. Network segmentation
Create at least three networks. The point is not realism for its own sake – it is that segmentation is the control you will spend most of your OT career testing, so your lab needs to have some.
| Zone | Example subnet | Purpose |
|---|---|---|
| Test / IT | 192.168.10.0/24 | Security workstation, analysis tools |
| OT DMZ | 192.168.20.0/24 | Jump host, controlled access |
| OT Control | 192.168.30.0/24 | HMI, PLC and industrial services |
Suggested lab architecture
CyberLabs OT Security Lab
+----------+-----------+
| Security Testing |
| Kali / Ubuntu |
| 192.168.10.50 |
+----------+-----------+
|
IT / Test Network
192.168.10.0/24
|
+----------+-----------+
| Firewall / Router |
| Segmentation Rules |
+----------+-----------+
|
OT DMZ
192.168.20.0/24
|
+----------+-----------+
| Jump Host / |
| Engineering PC |
| Windows |
+----------+-----------+
|
OT Control VLAN
192.168.30.0/24
|
+-------+-------+
| |
+-------+--------+ +---+------------+
| HMI / SCADA | | PLC Simulator |
| Simulator | | Modbus/TCP |
+----------------+ +----------------+
Mapped onto the Purdue reference model, the test network stands in for Level 4 (enterprise), the OT DMZ for Level 3.5, and the control VLAN for Levels 1-2. You are building a miniature version of the zone-and-conduit structure that IEC 62443 describes.
Hypervisor isolation
This is the step people get wrong, and it is the one that matters most.
- VMware Workstation: put each OT segment on a LAN Segment, not a NAT or host-only network. LAN segments have no connectivity to the host or the physical network.
- VirtualBox: use Internal Network adapters (
intnet) for the OT DMZ and OT Control segments. - Give the firewall VM one adapter per zone, plus one NAT or bridged adapter only if you need internet access for updates on the test network. Turn that adapter off during testing.
- The OT Control segment should have no default route to the internet at any point in the build.
Do not bridge the OT lab to your home or work network. A misconfigured Modbus write is harmless in an isolated segment and considerably less harmless if the segment happens to reach a real device on the same broadcast domain.
Monitoring interface
Add a second interface to your monitoring VM, attach it to the OT Control segment, and enable promiscuous mode:
- VMware: set the virtual network to allow promiscuous mode (on Linux hosts this may require permissions on
/dev/vmnet*). - VirtualBox: set Promiscuous Mode to Allow All on that adapter.
This is your virtual SPAN port. When you later add a physical managed switch, the same role is served by a mirrored port – the analysis workflow does not change.
6. Firewall policy
Begin with a default-deny design on every inter-zone interface. Then add only the flows the process actually needs.
| Source | Destination | Service | Action |
|---|---|---|---|
| Test workstation | Jump host | RDP / SSH (management only) | Allow |
| Jump host | HMI | HTTP/HTTPS or approved client port | Allow |
| HMI | PLC | Modbus/TCP 502 | Allow |
| Test workstation | PLC | Any | Deny (initially) |
| OT Control | Internet | Any | Deny |
| Any | Any | Any | Deny and log |
Enable logging on the final deny rule. Those logs are the evidence you will use to prove – or disprove – that segmentation works, and they are the raw material for detection rules later.
Two habits worth forming now:
- Write the rule intent down. A firewall rule without a documented business or process justification is a rule nobody will dare remove in three years’ time.
- Test the deny rules, not just the allow rules. Most segmentation findings in real assessments come from paths nobody intended to create – a management VLAN with a route it should not have, a dual-homed workstation, a vendor remote-access appliance sitting quietly in the DMZ.
This section sets up a later CyberLabs article: Can the IT Network Reach the PLC? Testing OT Segmentation Safely.
7. Install the PLC simulator
Install OpenPLC Runtime on a Linux VM in the OT Control segment (192.168.30.20 in the examples below). The runtime exposes a Modbus/TCP server on port 502 and a web management interface on port 8080.
Build a small, visible process rather than manipulating anonymous registers. A tank makes a good first example because the register values mean something a reader can picture.
Suggested register map
| Modbus object | OpenPLC address | Meaning |
|---|---|---|
| Coil 0 | %QX0.0 | Pump start/stop command (written by HMI) |
| Coil 1 | %QX0.1 | Pump running status |
| Coil 2 | %QX0.2 | High-level alarm |
| Holding register 0 | %QW0 | Tank level, 0-1000 (0.0-100.0 %) |
| Holding register 1 | %QW1 | Temperature x 10 (e.g. 215 = 21.5 degC) |
In OpenPLC’s default mapping, a Modbus master can write coils (%QX) and holding registers (%QW) and read discrete inputs (%IX) and input registers (%IW). Because the HMI writes the start command to coil 0, the PLC program must never assign to %QX0.0 – it only reads it.
Illustrative Structured Text
PROGRAM main
VAR
PumpCmd AT %QX0.0 : BOOL; (* command from HMI *)
PumpRun AT %QX0.1 : BOOL; (* status to HMI *)
HighAlarm AT %QX0.2 : BOOL;
Level AT %QW0 : INT := 400; (* 40.0 % *)
Temp AT %QW1 : INT := 215; (* 21.5 degC *)
Tick : TON;
END_VAR
Tick(IN := NOT Tick.Q, PT := T#500ms);
IF Tick.Q THEN
IF PumpCmd THEN
Level := Level + 10; (* pump fills the tank *)
ELSE
Level := Level - 5; (* steady draw-off *)
END_IF;
IF Level > 1000 THEN Level := 1000; END_IF;
IF Level < 0 THEN Level := 0; END_IF;
END_IF;
PumpRun := PumpCmd;
HighAlarm := Level > 850;
END_PROGRAM
Compile it in the OpenPLC Editor, upload it to the runtime and start the PLC. Confirm operation from the command line before involving the HMI:
# Read holding registers 0 and 1 (tank level, temperature)
mbpoll -a 1 -t 4 -r 1 -c 2 192.168.30.20
# Read the three coils
mbpoll -a 1 -t 0 -r 1 -c 3 192.168.30.20
Watch the level climb and fall as you set coil 0. That single command – one write, one process change, no authentication anywhere – is the whole argument for OT segmentation in a single line of terminal output.
8. Configure the HMI
Install ScadaBR or FUXA on a VM in the OT Control segment, add the PLC as a Modbus/TCP data source, and build a small operator screen showing:
- Tank level (bar or gauge, 0-100 %)
- Pump state (running / stopped)
- Alarm state (high level)
- Start and stop controls
- Communications status
Set the polling interval to something realistic – 500 ms to 2 seconds. The polling rate matters later: a steady, predictable poll cycle is what makes an OT baseline so much easier to profile than IT traffic, and it is what makes anomalies stand out.
Screenshots of this screen will make every future article more concrete, so it is worth spending twenty minutes making it look like an operator screen rather than a debug page.
9. Establish normal traffic
Before you attack anything, record what “normal” looks like. This is the single most valuable artefact in the lab.
Start a capture on the monitoring interface and let the lab run for at least fifteen minutes of ordinary operation – HMI polling, an operator starting and stopping the pump, an engineer logging into the jump host, a denied connection or two.
Capture on the OT Control segment:
sudo tcpdump -i eth1 -w ot-baseline-$(date +%Y%m%d).pcap
You should be able to identify:
- ARP, and its regular background pattern
- DNS, if present at all (in many control segments it is not)
- Modbus/TCP sessions on port 502, and which function codes appear
- The HMI polling cycle and its interval
- Engineering workstation access to the jump host
- Firewall allows and denies
Useful Wireshark display filters:
mbtcp # all Modbus/TCP
modbus.func_code == 3 # read holding registers
modbus.func_code in {5 6 15 16} # writes - the ones that change the process
tcp.flags.syn == 1 && tcp.flags.ack == 0
If you have Zeek running on the monitoring interface, its Modbus analyser will produce a modbus.log recording each request with its function code – a far more practical baseline format than raw packets when you start writing detections.
Record, in writing:
- Which devices talk to which, and on what ports
- Which Modbus function codes are legitimate on this network
- The expected polling interval
- Which hosts should never originate a write
Save the clean baseline PCAP somewhere separate from the VM images. Every later article – asset discovery, unauthorised command detection, segmentation testing – compares against this file.
10. Recovery and reset
A lab you cannot restore is a lab you will be reluctant to test in, which defeats the purpose.
Snapshot every VM in a known-good state, after configuration but before any testing. Name them clearly (baseline-2026-07-28, not Snapshot 3).
Export the firewall configuration. In pfSense and OPNsense this is a single XML file. Keep it outside the VM.
Back up the PLC project – the OpenPLC Editor project file and the compiled program.
Back up the HMI project – ScadaBR and FUXA both support project export.
Record IP addresses, VLANs and credentials in one document. Use throwaway credentials in the lab and never reuse anything from a real environment.
Practise the restore. Deliberately break something, revert to the snapshot, and confirm the lab comes back cleanly. The first time you do this should not be after you have accidentally corrupted a controller image.
The same discipline scales to real work: known-good configuration backups, tested restore procedures and a documented rollback plan are what turn a risky assessment into an acceptable one.
11. Validation tests
Work through this checklist before declaring the lab finished.
- [ ] The test workstation cannot reach the PLC directly on port 502
- [ ] The jump host can reach only the approved services on the OT DMZ and control segments
- [ ] The HMI can read the PLC (function codes 1, 3 succeed)
- [ ] The HMI can issue approved control commands (function code 5 succeeds)
- [ ] Denied connections appear in the firewall logs with source, destination and port
- [ ] Wireshark on the monitoring interface captures OT Control traffic that is not addressed to it
- [ ] Every VM can be restored from its snapshot
- [ ] No OT segment has a route to the internet
- [ ] The lab has no unintended connection to a production, home or corporate network
Verify the first item explicitly rather than assuming it:
# From the test workstation - this must fail
nc -vz 192.168.30.20 502
Then check that the attempt appears in the firewall log. A deny you cannot see in the logs is a control you cannot evidence.
12. What comes next
With the lab built, the following walkthroughs all become reproducible:
- Passive OT asset discovery with Wireshark
- Understanding Modbus/TCP traffic
- Safely testing IT-to-OT segmentation
- Detecting unauthorised Modbus commands
- Auditing an industrial switch
- Building an OT traffic baseline
Each will refer back to this article rather than repeating the build.
Downloads
- OT lab build checklist
- IP address and zone worksheet
- Firewall validation matrix
- Blank network diagram
- Lab safety checklist
Could an unintended pathway exist in your OT environment?
A lab shows you what segmentation looks like when it is designed deliberately. Real environments accumulate exceptions: a vendor remote-access appliance, a dual-homed workstation, a temporary rule that outlived the project that needed it.
CyberLabs OT security assessments examine network segmentation, remote-access pathways, industrial device exposure and monitoring controls, while accounting for operational availability and recovery requirements.