Snort IDS between PLC and HMI (Modbus)
Created Updated
This run-through is about how to use Snort to catch sketchy Modbus/TCP traffic in a small ICS lab.
Main goals:
- Catch non-Modbus junk on port
502 - Catch
Write Single Registerattempts (Function Code6) - Catch illegal values being written to a PLC register (traffic light logic)
Setup (quick)
- Snort config checked in
snort.lua HOME_NETset to my lab subnetEXTERNAL_NETset toany- Rules stored in
local.rules
Rule syntax check:
sudo snort -T -c /etc/snort/snort.lua
Start Snort in passive mode:
sudo snort -c /etc/snort/snort.lua -i eth0 --daq pcap --daq-mode passive -l /var/log/snort/
Passive mode keeps things safe and observable while testing.
Part 1A - Detect non-Modbus traffic on TCP/502
Rule:
alert tcp any any -> any 502 (msg:"Non-Modbus traffic on port 502 - Invalid Protocol ID"; byte_test:2,!=,0,2; sid:1000001; rev:1;)
- In Modbus/TCP, MBAP Protocol ID (bytes 2-3) should be
0x0000 - If it is non-zero, that payload is not valid Modbus
Test packet (crafted bad Protocol ID):
echo -e "\x00\x01\x00\x01\x00\x06\x01\x03\x00\x00\x00\x0A" | nc -w1 10.211.55.6 502
Result:
- Built-in Snort Modbus inspector alert fired
- Custom rule
sid:1000001also fired
That is a solid confirmation the rule is doing its job.
Part 1B - Detect Write Single Register (Function Code 6)
Rule:
alert tcp any any -> any 502 (msg:"Modbus Write Single Register - Function Code 6"; byte_test:1,=,6,7; sid:1000002; rev:1;)
- Byte
7of the Modbus frame is the function code - Value
6meansWrite Single Register
Test packet:
echo -e "\x00\x01\x00\x00\x00\x06\x01\x06\x00\x01\x00\xFF" | nc -w1 10.211.55.6 502
Result:
sid:1000002fired correctly
Part 1C - Same detection, cleaner rule (Modbus inspector)
Instead of raw byte offsets, I rewrote Part 1B using Snort's Modbus inspector keyword:
alert tcp any any -> any 502 (msg:"Modbus Write Single Register - Function Code 6"; modbus_func:6; sid:1000002; rev:1;)
Why this is nicer:
- Reads like intent, not byte math
- Easier to maintain
- Less likely to confuse future-you during tuning
Part 2 - Detect illegal values in a PLC register
Now the practical OT defense piece: detect writes that break process logic.
For a traffic light register with legal values 0-3, alert on anything greater than 3.
Final rule (targeting register 23 / %QW23):
alert tcp any any -> any 502 (msg:"Illegal value written to Modbus register 23 - traffic light"; modbus_func:6; byte_test:2,=,23,8; byte_test:2,>,3,10; sid:1000003; rev:1;)
What this checks:
modbus_func:6-> write single register onlybyte_test:2,=,23,8-> target register address 23byte_test:2,>,3,10-> written value must be greater than 3 to alert
Validation flow:
- Send legal value (for example
2) -> no alert - Send illegal value (for example
99) -> alert fires
Result:
- Snort fired only on illegal write activity
- No alert on legal writes
- Good signal quality for this lab scenario
Attack simulation note
I used a quick pymodbus write to push an illegal value into the PLC register.
That simulates a real ICS attack pattern: attacker injects out-of-range control values to mess with physical behavior.
Snort caught it immediately with sid:1000003.
Final ruleset
alert tcp any any -> any 502 (msg:"Non-Modbus traffic on port 502 - Invalid Protocol ID"; byte_test:2,!=,0,2; sid:1000001; rev:1;)
alert tcp any any -> any 502 (msg:"Modbus Write Single Register - Function Code 6"; modbus_func:6; sid:1000002; rev:1;)
alert tcp any any -> any 502 (msg:"Illegal value written to Modbus register 23 - traffic light"; modbus_func:6; byte_test:2,=,23,8; byte_test:2,>,3,10; sid:1000003; rev:1;)
Takeaways
- Start passive and verify visibility first
- Build rules in small chunks and validate each one
- Prefer protocol-aware keywords when possible (
modbus_func) - Write policy rules tied to process constraints, not just signatures
- Always test both positive and negative cases (alert + no-alert)
That is how you keep detections useful and avoid noisy IDS output in ICS labs.
Comments
No comments yet.
Sign in to comment, reply, vote, or delete your own comments. Anyone can read comments.