Transport
TCP (Modbus TCP) / Serial RS-485 (RTU)
Security
None (no auth, no encryption)
Standard
Modbus.org open standard
Deployment
~90% of OT environments
Modbus TCP Packet Structure
// Modbus TCP ADU (Application Data Unit)
Transaction ID (2B)
Protocol ID (2B) = 0x0000
Length (2B)
Unit ID (1B)
FC (1B)
Data (variable)
FC = Function Code | Unit ID = Slave address (1-247) | Protocol ID always 0x0000 for Modbus
Key Function Codes
| FC (Hex) | Name | Operation | Security Risk |
| 0x01 | Read Coils | Read discrete output status | Low — read only |
| 0x02 | Read Discrete Inputs | Read discrete input status | Low — read only |
| 0x03 | Read Holding Registers | Read analog output values | Medium — reveals process values |
| 0x04 | Read Input Registers | Read analog input values | Medium — reveals sensor data |
| 0x05 | Write Single Coil | Force single output ON/OFF | HIGH — direct control |
| 0x06 | Write Single Register | Write single holding register | HIGH — setpoint manipulation |
| 0x0F | Write Multiple Coils | Force multiple outputs | CRITICAL — mass control |
| 0x10 | Write Multiple Registers | Write multiple registers | CRITICAL — mass setpoint change |
| 0x2B | Read Device ID (FC43) | Read device identification | Medium — reconnaissance |
Security Vulnerabilities
No Authentication
Any device on the network can send Modbus commands to any PLC. There is no username, password, or certificate — the protocol was designed for isolated serial networks.
MITRE ATT&CK ICS: T0855 — Unauthorized Command Message
No Encryption
All Modbus traffic is plaintext. An attacker with network access can read all process values and inject commands. Man-in-the-middle attacks are trivial.
MITRE ATT&CK ICS: T0830 — Man in the Middle
No Message Integrity
Modbus TCP has no message authentication code (MAC). Packets can be replayed or modified in transit without detection. RTU uses CRC but only for error detection, not authentication.
MITRE ATT&CK ICS: T0831 — Manipulation of Control
Detection Logic
// Detect unauthorized Modbus write commands
modbus.function_code IN [0x05, 0x06, 0x0F, 0x10]
AND src_ip NOT IN authorized_scada_masters
AND dst_port == 502
→ ALERT: Unauthorized Modbus Write [T0855]
// Detect Modbus reconnaissance sweep
modbus.function_code == 0x2B // FC43 Device ID
AND COUNT(dst_ip) > 5 WITHIN 60s
→ ALERT: Modbus Asset Enumeration [T0846]
// Detect Modbus replay attack
modbus.transaction_id == previous_transaction_id
AND time_delta < 100ms
→ ALERT: Possible Modbus Replay Attack
Transport
TCP, UDP, Serial
Security
Optional SA v5 (rarely used)
Sector
Electric utilities, water/wastewater
Attack
Industroyer/Crashoverride
DNP3 Layer Structure
// DNP3 Application Layer → Transport Layer → Data Link Layer
Start (0x0564)
Length
Control
Destination (2B)
Source (2B)
Transport + App Layer
CRC
App Layer contains: FIR/FIN bits, Sequence, Function Code, Object Headers, Data Objects
Critical Function Codes
| FC | Name | Direction | Risk |
| 0x01 | READ | Master → Outstation | Low |
| 0x02 | WRITE | Master → Outstation | Medium |
| 0x03 | SELECT | Master → Outstation | High — SBO step 1 |
| 0x04 | OPERATE | Master → Outstation | CRITICAL — SBO step 2, executes control |
| 0x05 | DIRECT OPERATE | Master → Outstation | CRITICAL — immediate control, no SBO |
| 0x81 | RESPONSE | Outstation → Master | Low |
| 0x82 | UNSOLICITED RESPONSE | Outstation → Master | Low |
Industroyer/Crashoverride Attack Pattern
// Industroyer DNP3 attack sequence (Ukraine 2016)
1. STARTDT_ACT → Establish session with RTU
2. READ (FC01) → Enumerate available data objects
3. SELECT (FC03) → Select breaker control object
4. OPERATE (FC04) → Open circuit breaker
5. Repeat for all RTUs in substation
// Detection: STARTDT from unauthorized master IP
dnp3.function == STARTDT_ACT
AND src_ip NOT IN authorized_dnp3_masters
→ ALERT: Unauthorized DNP3 Session [T0855]
Transport
TCP via ISO-TSAP / COTP
Security
None (S7comm) / Broken (S7comm+)
Sector
Manufacturing, process industry
Vendor
Siemens (proprietary)
S7comm PDU Types
| PDU Type | Name | Function Codes | Risk |
| 0x01 | Job Request | Read/Write/Download/Upload | High — all control operations |
| 0x02 | Ack | Acknowledgement | Low |
| 0x03 | Ack-Data | Response with data | Low |
| 0x07 | Userdata | Diagnostics, programming | Medium |
Stuxnet Attack Pattern
// Stuxnet used S7comm to:
1. Connect to S7-315 and S7-417 PLCs via Step 7 software
2. Read PLC program blocks (FC/FB/DB)
3. Inject malicious code blocks (OB35 interrupt)
4. Intercept S7comm to hide malicious blocks from operators
5. Manipulate centrifuge speeds while reporting normal values
// Detection: S7 program download outside maintenance window
s7.pdu_type == 0x01 AND s7.function == 0x05 // Download block
AND NOT maintenance_window_active
AND src_ip NOT IN authorized_engineering_workstations
→ ALERT: Unauthorized S7 Program Download [T0843]
Transport
TCP, HTTPS, WebSockets
Security
Optional (often None in practice)
Attack
PIPEDREAM/INCONTROLLER (2022)
Sector
All modern OT environments
Security Modes
| Security Mode | Security Policy | Auth | Encryption | Risk |
| None | None | ❌ | ❌ | CRITICAL — no security |
| Sign | Basic256Sha256 | ✓ | ❌ | Medium — integrity only |
| SignAndEncrypt | Basic256Sha256 | ✓ | ✓ | Low — recommended |
// PIPEDREAM OPC-UA exploitation pattern
1. Discover OPC-UA servers (port 4840 scan)
2. Connect with None security mode (no auth required)
3. Browse address space to enumerate tags
4. Write to control tags (e.g., valve position, pump speed)
5. Subscribe to data changes for persistent access
// Detection: OPC-UA None security mode connection
opcua.security_policy == "None"
AND opcua.message_security_mode == 1 // None
→ ALERT: OPC-UA Insecure Connection [T0866]
// Detection: OPC-UA write to control tags
opcua.service == "Write"
AND opcua.node_id IN critical_control_tags
AND src_ip NOT IN authorized_scada_clients
→ ALERT: Unauthorized OPC-UA Write [T0855]
Vendor
Rockwell Automation (primary)
Attack
PIPEDREAM INCONTROLLER
// Detect unauthorized CIP program download
enip.cip_service IN [0x4B, 0x4C] // Download/Upload
AND src_ip NOT IN authorized_engineering_workstations
AND NOT maintenance_window_active
→ ALERT: Unauthorized EtherNet/IP Program Transfer [T0843]
// Detect CIP identity object enumeration (reconnaissance)
enip.cip_service == 0x01 // Get Attribute All
AND enip.cip_class == 0x01 // Identity Object
AND COUNT(dst_ip) > 5 WITHIN 60s
→ ALERT: EtherNet/IP Asset Enumeration [T0846]
Sector
Electric utilities (Europe)
Security
None (IEC 62351 optional)
Attack
Industroyer (Ukraine 2016)
// Industroyer IEC 104 attack pattern
1. STARTDT_ACT → Activate data transfer
2. C_IC_NA_1 (ASDU 100) → General interrogation
3. C_SC_NA_1 (ASDU 45) → Single command (open breaker)
4. C_DC_NA_1 (ASDU 46) → Double command
// Detection: IEC 104 control command from unauthorized source
iec104.asdu_type IN [45, 46, 47, 48] // Control ASDUs
AND src_ip NOT IN authorized_control_centers
→ ALERT: Unauthorized IEC 104 Control Command [T0855]
Sector
Manufacturing, process
Port
UDP/34964, Ethertype 0x8892
Security
Limited (IEC 62443-3-3)
GOOSE Port
Ethertype 0x88B8 (multicast)
Security
IEC 62351 (optional)
Transport
DCOM/RPC (dynamic ports)
Security
DCOM security (often misconfigured)
Port
TCP/1883 (plain), TCP/8883 (TLS)
Security
Optional TLS + username/password
Security
None (BACnet/SC optional)
Sector
Buildings, facilities, hospitals
Transport
Twisted pair, IP (LonIP)