Wiki / Protocols

S7comm

Siemens' proprietary PLC protocol for the S7 family, carried over ISO-on-TCP on port 102. Legacy S7comm is unauthenticated; read a data block, write a merker, stop the CPU.

S7comm over ISO-on-TCP
TCP :102
TPKT + COTP (RFC 1006)
connect: rack/slot TSAP
S7comm (ROSCTR)
Read/Write DB, M, I, Q
PLC start/stop, SZL
S7comm does not run directly on TCP. A client first opens an ISO-on-TCP (RFC 1006) session on port 102, negotiating a COTP connection to a specific rack and slot (the CPU), then negotiates S7 PDU sizes, and only then issues S7 jobs: read and write memory areas (data blocks, merkers, inputs, outputs), start or stop the CPU, and read the SZL system status lists.

S7comm is Siemens' proprietary communication protocol for the S7 family of PLCs (S7-300, S7-400, and, in its S7comm-plus form, S7-1200 and S7-1500). It is how a TIA Portal engineering station, an HMI, or a SCADA server reads and writes a Siemens controller. Like the other field protocols, classic S7comm was built for a trusted network: there is no real authentication, and the optional "password" protection is weak.

What it is

S7comm does not sit directly on TCP. It is layered on ISO-on-TCP (RFC 1006), which itself carries the ISO transport protocol COTP, all on port 102:

TCP :102
  -> TPKT (RFC 1006 framing)
     -> COTP  (connect to a rack/slot TSAP = the CPU)
        -> S7comm  (S7 jobs / ack-data)

A session is set up in two negotiations: a COTP connection request that names the target rack and slot (which CPU in the rack), then an S7 Setup Communication that agrees on PDU sizes. Only then can you issue S7 functions. The important ones read and write memory areas: data blocks (DB), merkers/flags (M), inputs (I), outputs (Q), timers and counters; control the CPU (PLC STOP / PLC START); upload and download program blocks; and read the SZL (System Status List) for CPU type, firmware version, and diagnostics.

Why it matters

A Siemens PLC running S7comm is directly readable and writable by anyone who can reach port 102 and speak the ISO/COTP dance. Reading a data block leaks process values and logic constants; writing a merker or a DB byte can flip an internal flag the ladder logic acts on (an override bit, a setpoint); a PLC STOP halts the controller outright. The SZL read is the fingerprint step: it tells you the exact CPU and firmware, which decides whether you face legacy S7comm (open) or S7comm-plus (integrity-protected). None of the read or write functions checks an identity on classic S7comm.

How to work it

The reference library is snap7 (with the python-snap7 bindings), which handles the ISO/COTP/S7 stack for you.

  1. Find S7 PLCs and fingerprint them:
nmap -p 102 --script s7-info --open <subnet>
  1. Connect to a CPU by ip, rack, and slot, then read the SZL for CPU and firmware:
import snap7
c = snap7.client.Client()
c.connect("<ip>", 0, 1)          # rack 0, slot 1 (typical S7-300)
print(c.get_cpu_info())
  1. Read a data block to pull process data:
data = c.db_read(1, 0, 32)       # DB1, start 0, 32 bytes
  1. Write a memory area to change controller behavior (a merker bit, a DB value), knowing exactly what the logic does with it. c.plc_stop() halts the CPU.
  2. Capture a live session in Wireshark (the s7comm and cotp dissectors) to map jobs and replay or study them.

Pitfalls

  • Rack and slot must be right. S7-300 is usually rack 0 / slot 2, S7-1200/1500 often rack 0 / slot 1. A wrong slot fails the COTP connect before you ever send an S7 job.
  • S7comm is not S7comm-plus. The S7-1200/1500 v4+ speak S7comm-plus, which adds integrity protection and anti-replay; the classic snap7 read/write path does not simply work against them. Read the SZL firmware first.
  • This is live plant control. A merker or DB write, and especially a PLC STOP, moves or halts a real process; stay on the lab or a system you own.
  • Port 102 open is not proof of S7comm alone: other ISO-on-TCP services share it. Confirm with the COTP/S7 handshake.

Further reading