[CWE-787/CWE-121] Pre-auth stack buffer overflow in S7 Write handler — snap7 server
Finding ID: SNAP7-03 Severity: Critical (unauthenticated remote, potential RCE)
Component: snap7 S7 server (libsnap7) — TS7Worker::PerformFunctionWrite()
Summary
An unauthenticated remote attacker who can open a TCP connection to a snap7 S7
server (default port 102/tcp) can overflow a fixed 20-element pointer array on
the worker thread's stack by sending a single S7 Write request whose item
count byte is larger than 20. The write handler loops over the attacker's
ItemsCount (a full 0–255 byte) with no upper bound, storing a pointer into
ReqData[c] for each iteration; the array ReqData holds only MaxVars = 20
entries. Overwriting past it corrupts adjacent stack memory (the response
buffer, saved registers, return address) with attacker-influenced pointer
values, giving a remote crash and a plausible path to code execution.
The sibling Read handler caps this exact value (if (ReqParams->ItemsCount>MaxVars) ReqParams->ItemsCount=MaxVars;);
the Write handler is missing that clamp.
Affected Version
SCADACS/snap7 at commit f6ff90317ca5d54250f4dcd29209689a74e26d82 (S7 server core
src/core/s7_server.cpp; codebase descends from Snap7 1.4.x). Any build that
exposes TSnap7Server / Srv_Start*.
Reachability
REACHABLE, unauthenticated. S7comm has no password/auth in the base protocol.
Path: TCP connect → COTP Connection Request/Confirm → S7 request PDU with
function pduFuncWrite (0x05).
ExecuteRecv → IsoPerformCommand → PerformPDURequest (case pduFuncWrite) → PerformFunctionWrite.
No registered data area is required — the overflow occurs in stage-1 parameter
setup, before any area lookup.
Technical Details
src/core/s7_types.h:
const int MaxVars = 20;
typedef PReqFunWriteDataItem TReqFunWriteData[MaxVars]; // 20 pointers
struct TReqFunWriteParams { byte FunWrite; byte ItemsCount; TReqFunWriteItem Items[MaxVars]; };
src/core/s7_server.cpp, PerformFunctionWrite():
TReqFunWriteData ReqData; // 20-element stack array (line 844)
...
ItemsCount = ReqParams->ItemsCount; // attacker byte 0..255, NO clamp (line 860)
for (c = 0; c < ItemsCount; c++) {
ReqData[c] = PReqFunWriteDataItem(pbyte(PDUH_in)+StartData); // OOB write at c>=20 (line 864)
...
}
CheckPDU_in() only verifies ParLen + DataLen + ReqHeaderSize == PayloadSize;
it never bounds ItemsCount, so ItemsCount = 0xFF passes framing validation.
Data flow: TCP bytes → S7 PDU param byte ItemsCount → loop bound → ReqData[c]
stack write past a 20-entry array (160 bytes) → up to 235 pointer slots (~1.8 KB)
of stack corruption.
Proof of Concept
poc/poc_snap7_write_overflow.cpp starts the real TSnap7Server (built with
-fsanitize=address) on 127.0.0.1, performs the COTP handshake, and sends one
12-byte S7 write PDU with ItemsCount = 0xFF. AddressSanitizer output
(poc/crash/SNAP7-03_write_asan.txt):
ERROR: AddressSanitizer: stack-buffer-overflow ... WRITE of size 8
#0 TS7Worker::PerformFunctionWrite() s7_server.cpp:864
#1 TS7Worker::PerformPDURequest(int&) s7_server.cpp:336
#2 TS7Worker::IsoPerformCommand(int&) s7_server.cpp:307
#3 TIsoTcpWorker::ExecuteRecv()
This frame has 3 object(s):
[32, 192) 'ReqData' <== Memory access at offset 192 overflows this variable
[256, 4352) 'Answer'
Crashing input (raw S7 PDU): poc/corpus/seed_write_overflow.bin
(32 01 00 00 00 00 00 02 00 00 05 FF). Also reproduced by the AFL harness
(poc/fuzz_s7_pdu).
Impact
CVSS v3.1: 9.8 Critical — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H.
Unauthenticated remote stack corruption in a PLC-facing server that frequently
runs with high privilege. Minimum impact is reliable remote crash (DoS of the
PLC/gateway); the overwritten values are attacker-influenced heap pointers, so
control-flow hijack (RCE) is plausible depending on stack layout / mitigations.
Suggested Remediation
Clamp the count before the loop, mirroring the Read handler:
if (ReqParams->ItemsCount > MaxVars) ReqParams->ItemsCount = MaxVars;
ItemsCount = ReqParams->ItemsCount;
and additionally validate that each item's declared offset/length stays within
the received PDU.
Disclosure Timeline
- Discovered: 2026-07-03 (local static analysis + ASan PoC)
- Vendor notified: PENDING — operator to contact maintainer (no contact made)
- CVE requested: pending
- Public disclosure target: +90 days after vendor contact
[CWE-787/CWE-121] Pre-auth stack buffer overflow in S7 Write handler — snap7 server
Finding ID: SNAP7-03 Severity: Critical (unauthenticated remote, potential RCE)
Component: snap7 S7 server (
libsnap7) —TS7Worker::PerformFunctionWrite()Summary
An unauthenticated remote attacker who can open a TCP connection to a snap7 S7
server (default port 102/tcp) can overflow a fixed 20-element pointer array on
the worker thread's stack by sending a single S7 Write request whose item
count byte is larger than 20. The write handler loops over the attacker's
ItemsCount(a full 0–255 byte) with no upper bound, storing a pointer intoReqData[c]for each iteration; the arrayReqDataholds onlyMaxVars = 20entries. Overwriting past it corrupts adjacent stack memory (the response
buffer, saved registers, return address) with attacker-influenced pointer
values, giving a remote crash and a plausible path to code execution.
The sibling Read handler caps this exact value (
if (ReqParams->ItemsCount>MaxVars) ReqParams->ItemsCount=MaxVars;);the Write handler is missing that clamp.
Affected Version
SCADACS/snap7 at commit
f6ff90317ca5d54250f4dcd29209689a74e26d82(S7 server coresrc/core/s7_server.cpp; codebase descends from Snap7 1.4.x). Any build thatexposes
TSnap7Server/Srv_Start*.Reachability
REACHABLE, unauthenticated. S7comm has no password/auth in the base protocol.Path: TCP connect → COTP Connection Request/Confirm → S7 request PDU with
function
pduFuncWrite (0x05).ExecuteRecv → IsoPerformCommand → PerformPDURequest (case pduFuncWrite) → PerformFunctionWrite.No registered data area is required — the overflow occurs in stage-1 parameter
setup, before any area lookup.
Technical Details
src/core/s7_types.h:src/core/s7_server.cpp,PerformFunctionWrite():CheckPDU_in()only verifiesParLen + DataLen + ReqHeaderSize == PayloadSize;it never bounds
ItemsCount, soItemsCount = 0xFFpasses framing validation.Data flow: TCP bytes → S7 PDU param byte
ItemsCount→ loop bound →ReqData[c]stack write past a 20-entry array (160 bytes) → up to 235 pointer slots (~1.8 KB)
of stack corruption.
Proof of Concept
poc/poc_snap7_write_overflow.cppstarts the realTSnap7Server(built with-fsanitize=address) on 127.0.0.1, performs the COTP handshake, and sends one12-byte S7 write PDU with
ItemsCount = 0xFF. AddressSanitizer output(
poc/crash/SNAP7-03_write_asan.txt):Crashing input (raw S7 PDU):
poc/corpus/seed_write_overflow.bin(
32 01 00 00 00 00 00 02 00 00 05 FF). Also reproduced by the AFL harness(
poc/fuzz_s7_pdu).Impact
CVSS v3.1: 9.8 Critical —
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H.Unauthenticated remote stack corruption in a PLC-facing server that frequently
runs with high privilege. Minimum impact is reliable remote crash (DoS of the
PLC/gateway); the overwritten values are attacker-influenced heap pointers, so
control-flow hijack (RCE) is plausible depending on stack layout / mitigations.
Suggested Remediation
Clamp the count before the loop, mirroring the Read handler:
and additionally validate that each item's declared offset/length stays within
the received PDU.
Disclosure Timeline