⚠️ For defensive/educational purposes only. Sniff only networks you own or are explicitly authorized to test. Unauthorized network monitoring is illegal in most jurisdictions.
The uncomfortable truth about your factory floor
If your plant uses Modbus TCP — and statistically, it probably does — every register read, every coil write, every sensor value is flying across your network in plaintext. No encryption. No authentication. No signature. Nothing.
Modbus was designed in 1979 by Modicon for serial communication between a PLC and a few field devices on a dedicated cable. The threat model was "someone might physically tap the wire." The solution was "don't let strangers into the control room."
Forty-five years later, that same protocol is running over your corporate VLAN, talking to cloud historians, and occasionally — if your IT/OT segmentation has gaps — reachable from the internet.
Let me show you what that looks like from the wire.
The 5-line sniffer
This is a defensive monitoring tool. Same code your blue team would use to baseline normal traffic and detect anomalies. Requires scapy:
pip install scapy
from scapy.all import sniff, TCP, Raw
def show_modbus(pkt):
if TCP in pkt and pkt[TCP].dport == 502 and Raw in pkt:
payload = pkt[Raw].load
print(f"{pkt['IP'].src} → {pkt['IP'].dst}: {payload.hex()}")
sniff(filter="tcp port 502", prn=show_modbus, store=False)
Run it on a span port, a TAP, or a mirror VLAN, and within seconds you'll see something like this:
192.168.1.50 → 192.168.1.10: 0001000000060103006400 02
192.168.1.10 → 192.168.1.50: 00010000000701030441f00000
192.168.1.50 → 192.168.1.10: 00020000000601100065000102
Every byte tells a story. Let's decode the first packet.
Decoding what you just captured
The Modbus TCP frame format is documented in the spec (it's public — that's part of the problem):
Bytes 0-1: Transaction ID
Bytes 2-3: Protocol ID (always 0x0000 for Modbus)
Bytes 4-5: Length
Byte 6: Unit ID (slave address)
Byte 7: Function code
Byte 8+: Function-specific data
So 0001000000060103006400 02 decodes as:
| Bytes | Value | Meaning |
|---|---|---|
0001 |
TID 1 | Just a sequence number |
0000 |
0 | Modbus protocol marker |
0006 |
6 | 6 bytes follow |
01 |
Unit 1 | Slave address |
03 |
FC 03 | Read Holding Registers |
0064 |
100 | Starting at register 100 |
0002 |
2 | Read 2 registers |
The response (00010000000701030441f00000) gives you 4 bytes of data: 41f00000. Interpreted as a big-endian float32, that's 30.0 — probably a temperature reading.
You now know: a controller at .10 is reading temperature from .50 once per second. No credentials, no challenge, no audit log.
What an attacker sees (and you should too)
After 10 minutes of passive sniffing, you can typically reconstruct:
Network topology — every PLC, every HMI, every SCADA polling client and their IP addresses.
Device mapping — which Unit IDs exist, which function codes each device responds to, which slaves never reply (potential outdated documentation).
Register usage — by watching read/write patterns, you can guess which registers hold setpoints, which hold process values, and which control critical outputs. Cross-reference with public PLC documentation and you've reverse-engineered the plant.
Production data — actual temperatures, pressures, flow rates, motor speeds. If you're a competitor, this is your KPI scorecard. If you're a nation-state, this is your targeting data.
Timing patterns — when does the night shift start? When are setpoints changed? When does maintenance happen? Modbus traffic answers all of these.
None of this requires sophisticated tools. The sniffer above is 5 lines.
What attackers can actually do (the part nobody wants to publish)
I'm not going to give attack scripts. But you should understand the attack surface so you can defend it:
- Replay attacks — Modbus has no nonce, no timestamp, no session. Captured commands can be replayed verbatim.
- Man-in-the-middle injection — if an attacker can ARP-spoof onto the Modbus segment, they can rewrite values in transit.
- Unit ID spoofing — there is no source authentication. Any device on the segment can claim to be Unit 1.
- Function code abuse — Function 08 (diagnostics) on some devices includes "restart communications" and "force listen-only mode."
Real-world parallel: the 2016 Ukraine grid attack used malware called Industroyer (also known as CrashOverride) that spoke industrial protocols natively — IEC 60870-5-101/104 and IEC 61850 — to send legitimate-looking commands directly to substation equipment, bypassing the HMI layer entirely. No zero-days, no exotic exploits. Just protocol-level access on a network the attackers shouldn't have been on.
How to actually defend
Three layers, in order of impact:
1. Network segmentation (the only thing that actually matters)
If your Modbus traffic is reachable from the office VLAN, your guest WiFi, or — please no — the internet, nothing else you do matters. ISA/IEC 62443 zones and conduits, properly enforced firewalls, jump hosts, the whole drill.
Shodan currently shows tens of thousands of internet-exposed Modbus devices. Don't be on that list.
2. Modbus/TCP Security (Modbus Organization, 2018)
If you can't air-gap (and most plants can't), encrypt. Modbus/TCP Security is a published spec from modbus.org that wraps Modbus in TLS with X.509v3 certificate authentication, runs on port 802, and is supported by a growing number of modern controllers. The migration is non-trivial but real.
3. Passive monitoring + anomaly detection
This is where the sniffer code earns its keep — for defensive use. Baseline your normal traffic patterns: which clients talk to which slaves, which function codes appear, what register ranges get touched, at what cadence. Alert on anything outside baseline.
You don't need an expensive ICS IDS to start. You need 50 lines of Python and a discipline of regularly reviewing the logs.
What I built for this
I run automation projects at 100MW+ scale, and the "what's actually on my Modbus network right now" question came up often enough that I packaged my monitoring tooling into a product: Modbus Logger Pro. It auto-detects byte order across 50+ tested PLC models, logs structured traffic, and is built for the defensive side of the question this article raises.
But honestly — the 5 lines above are enough to start. Run them on your own network this week. You will be surprised by what you see.
TL;DR
- Modbus's original ADU has no built-in security — by design, in 1979. The spec is frozen for compatibility; the modern answer is a separate Modbus/TCP Security spec (2018) that wraps it in TLS, but adoption is still slow
- 5 lines of Python can decode every register read on your factory floor
- Defense is network segmentation first, TLS second, monitoring third
- Run the sniffer on your own authorized network to baseline what "normal" looks like — you can't detect anomalies without that
If this resonated, I'm writing more in this space — MQTT broker exposure next week, then OPC UA's surprisingly competent security design, then a CAN bus piece for the EV folks. Follow if that's your beat.
References & further reading:
- Modbus Application Protocol Specification V1.1b3 (Modbus Organization)
- NIST SP 800-82 Rev. 3: Guide to Operational Technology (OT) Security
- ISA/IEC 62443 series
- MITRE ATT&CK for ICS
Top comments (0)