Siemens S7 Detection blog image
Blog

Siemens S7 Detection After AA26-231A: What Production Traffic Actually Shows

Jori VanAntwerp
CEO and Founder at  || Web

For over two decades, Jori has enabled industrial and IT organizations to be successful in reducing risk, increasing compliance, and improving their overall security efforts. He has had the pleasure of working with companies such as Gravwell, Dragos, CrowdStrike, FireEye, McAfee, and is now CEO & Founder at EmberOT, a cybersecurity startup focused on making security a reality for critical infrastructure.

Siemens S7 detection moved up a lot of roadmaps this month. On August 19, 2026, the NSA, CISA, FBI, Department of Energy, and EPA published joint advisory AA26-231A, describing an active threat to Siemens S7 Series PLCs. Scope covers the S7-200, S7-300, S7-400, S7-1200, and S7-1500 families including F-series safety controllers, across critical manufacturing, energy, water and wastewater, chemical, food and agriculture, and commercial facilities, with potential exposure in the defense industrial base.

Siemens S7 detection involves identifying suspicious or unauthorized S7 controller activity by combining protocol-level operations, source-to-controller relationships, change-window context, and production traffic baselines.

The tradecraft is what makes this one interesting. Actors are using AI-generated exploitation scripts disguised as legitimate monitoring tools, finding targets through internet scanning services like Censys and ZoomEye, and building custom Python tooling on the snap7 and python-snap7 libraries to read and write PLC memory, configuration data, and ladder logic. Top mitigations are inventory, patching, getting PLCs off the internet, stronger access controls, monitoring for unauthorized activity, and hardening PLC services, protocols, and ladder logic. Reporting on the advisory maps the tooling to ATT&CK T1588.007, reads to T0893, and writes to T0821, Modify Controller Tasking. That read and write split is worth keeping in any taxonomy you build from this, because the two carry very different consequence. The agencies are explicit that PLC targeting is broader than Siemens.

Three boundaries matter before anyone writes a rule from it. The advisory describes reconnaissance and capability development, not confirmed operational effects. It names no actor. It publishes no malicious capture. So the mapping from described behavior to protocol semantics is yours to build, and how well you build it decides whether the detection works.

We went looking for what that mapping produces against real traffic, using a capture from an operating control environment at a major transportation authority whose engineering team supported this research.

What The Capture Actually Held

Classic S7comm traffic across seven client-side addresses and 47 controller-side addresses, carrying roughly 89,000 Read Var requests and roughly 1,300 Write Var requests. Those are request counts, not a tally of distinct physical actions, which is a distinction worth holding onto.

The environment was running normally while we captured it. Writes were highly repetitive and hit a small set of bit-level targets, which is what cyclic HMI and control behavior looks like. We found nothing in the capture that looked malicious. That isn’t the same as proving nothing was, and one capture doesn’t clear an environment.

Now put the advisory’s guidance next to those numbers. It calls for alerting on S7comm PUT/GET operations outside change windows, and on anomalous port 102 activity outside maintenance windows. That scoping is correct. It’s also carrying the entire recommendation.

Strip the change-window qualifier and the same rule fires on all 1,300 observed writes, in a single capture, on a network where nothing was wrong. So the real question isn’t whether to implement the guidance. It’s whether your environment has a defined change window at all, and whether it lives somewhere a detection can query at evaluation time. Plenty don’t. Where that’s the case, the recommendation has no filter to apply and quietly becomes an alert on normal operations.

The capture doubles as a negative baseline, which is the part we found most useful. Across all of it: no program downloads (0x1A through 0x1C), no program uploads (0x1D through 0x1F), no PLC Stop (0x29), no PI-Service (0x28), no Mode Transition (0x01). Detections built on those functions stayed silent in traffic that wasn’t attempting them. That’s the property that matters operationally. A detection that fires because it recognized an industrial protocol isn’t a detection. The inference doesn’t run backward either: every one of those operations has legitimate uses, and their absence here says nothing about intent when they do appear.

What Classic S7comm Gives You

Classic S7comm is unusually legible when a sensor can see it. Operations are named in the protocol instead of inferred from volume and timing.

Protocol ID is 0x32. Current Wireshark dissector source, packet-s7comm.c, defines these job function values. Treat that as implementation evidence from an open-source dissector, not as a Siemens specification.

FunctionValueSignificance
Mode Transition0x01Controller state change
Read Var0x04Routine data read
Write Var0x05Data write
Request Download0x1AStart of logic download to controller
Download Block0x1BLogic transfer
Download Ended0x1CEnd of logic download
Start Upload0x1DStart of logic retrieval from controller
Upload0x1ELogic transfer
End Upload0x1FEnd of logic retrieval
PI-Service0x28Program invocation
PLC Stop0x29Controller stop
Setup Communication0xF0Session negotiation

In visible classic S7comm, a program download identifies itself through explicit protocol functions. Starting from a named operation beats starting from a statistical outlier, and it’s why S7 is such a good case study for detection engineering. None of it carries over to S7CommPlus, TLS-protected traffic, proprietary extensions, or Siemens controller traffic generally.

Connection setup adds a little more. In common classic S7 patterns the destination TSAP’s first byte indicates a connection class, 0x01 usually programming device and 0x02 usually operator panel. Devices, routing, and implementations vary. The bigger caveat is that the client asks for that class, so it tells you what a host wanted to be treated as, not who it is. A programming-device connection from something with no engineering role is still worth a look.

What Siemens S7 Detection Requires in Production Traffic

Effective Siemens S7 detection depends on treating the protocol operation as the starting point, then checking whether the source, controller, timing, role, and authorization make sense.

For any given write, these are the questions that separate routine traffic from something worth an analyst’s attention:

  • Is this source new to this controller?
  • Is this the first write from this source and controller pair?
  • Is it touching an area the pair has never touched?
  • Is it inside a defined change or maintenance window?
  • Does this source have an engineering role at all?
  • Does the connection class match what this host is supposed to do?

You can’t answer any of them from one packet. They need a record of which relationships exist, what each normally does, and which are authorized. The protocol field is the trigger that sends you to that record, not the record itself.

Network monitoring supplies a good chunk of it. Relationships, asset context, observed operations, historical behavior, and first-seen or changed pairs all come out of traffic, and that’s the layer EmberOT is built around. Authorization status, engineering-role assignment, and change-window calendars come from outside the network, and detection gets meaningfully better when an environment can feed those in.

Where The Collection Path Changes The Answer

Some of the hardest problems here have nothing to do with protocol knowledge and everything to do with how traffic reaches the sensor.

Duplication. Monitoring paths duplicate traffic. Span and tap configurations routinely deliver the same frame more than once, and we have seen it run heavier on PROFINET than on TCP in the same environment. Any threshold written as a packet count is partly measuring your collection path rather than your network. A rule tuned for ten connection attempts in sixty seconds may be counting five real ones. Measure your own duplication before trusting a count-based threshold.

Long-lived sessions. OT sessions stay up for days or weeks. When a passive sensor restarts, everything already established becomes a midstream session to the new process. In our testing, default Suricata stream behavior didn’t alert on the pre-existing sessions we tested. Turning on stream.midstream: true with one-sided stream handling let the tested stream rules evaluate them. A rule that works perfectly against a fresh handshake can miss the identical command on a session older than your sensor.

Packet and stream inspection. The two failed in opposite directions in testing. Packet-context rules evaluated some traffic on pre-existing sessions but missed a pattern split across TCP segments. Stream-context rules reassembled the segmented content but depended on stream state and configuration. Running both cut the blind spots and generated overlapping events, so correlation and deduplication above the rule layer is part of the cost rather than a solved problem.

Field position versus byte pattern. We embedded a structurally complete PLC-Stop-shaped S7 telegram inside the data region of a synthetic Write Var packet. An unbounded raw-byte signature fired on it. The bytes were there. The operation wasn’t. Validating the protocol field beats scanning a payload for operation-shaped bytes, and any signature that looks right should be replayed against real traffic before you trust it.

Hunt Guidance and The Fields Underneath It

Public hunt guidance on this threat has been useful where it counts most, which is strategy. Focus on approved engineering systems. Build in change windows. Establish which sources should be talking to controllers at all. Treat snap7 as legitimate software that can be abused rather than as malware. Those hold regardless of anything below.

Some of the protocol-level mappings in circulation didn’t survive validation, and that failure mode is a quiet one.

Guidance representing controller start and restart as top-level classic S7 job function values like 0x2B and 0x2C doesn’t match current dissector modeling, which doesn’t list those among its job function definitions. Implementation evidence puts start and restart inside PI-Service 0x28 and its service parameters, with P_PROGRAM as the service. Program control runs through that mechanism in both directions: a published protocol walkthrough describes PLC Stop as an equivalent construct to PI-Service, carrying no parameter block, and names P_PROGRAM as its service.

Some block and CPU enumeration operations presented as top-level function indicators live under UserData function groups and subfunctions instead.

A hunt query built on the wrong field doesn’t error. It returns nothing, and nothing looks clean. A strategically sound idea can produce a false negative that reads as a good result, which is a lot harder to catch than a query that visibly breaks.

Two more interpretation traps belong here. A single access or protection error doesn’t establish credential probing, though repeated errors from an unexpected source might mean something. SNMP warm-start and cold-start traps can corroborate that a controller restarted without telling you which command did it. And production traffic carries plenty of legitimate UserData including normal cyclic service behavior, so guidance treating UserData as inherently interesting will bury you.

The Visibility Boundary

Everything above about legibility stops at classic S7comm.

S7CommPlus is what TIA Portal and HMI systems use with S7-1200 and S7-1500. From TIA Portal V17 that programming and HMI traffic can be secured with TLS, using certificates the CPU presents to identify itself. Siemens documents a secure mode allowing only TLS-protected PG/PC and HMI communication and a mixed mode that still permits legacy, with legacy requiring separate activation on V17 and later. Where only secure communication is permitted, the operation isn’t readable from the wire.

Two settings are worth auditing directly: whether legacy PG/HMI communication is enabled on controllers that don’t need it, and whether “Permit access with PUT/GET communication from remote partner” is on in the CPU’s protection settings, since that’s what lets classic S7comm reach an S7-1200 or S7-1500 at all.

Endpoint artifacts have shifted too. python-snap7 3.0 was rewritten as a pure Python implementation of the TPKT, COTP, and S7 stack and no longer needs the native Snap7 shared library. Any detection built on finding that binary will miss current versions. Older releases, standalone Snap7 deployments, and other packaged tooling can still leave native artifacts, so the artifact hunt keeps some value without being sufficient. Current project documentation also describes a client that picks the protocol suited to the controller, with some capabilities requiring an S7CommPlus connection.

A sensor without command semantics still sees endpoints, TCP relationships, session timing and duration, first-seen and changed relationships, volume, network zone, and asset context. Encryption hides the operation, not necessarily the relationship. That reduced picture answers every question in the list above except the one that needs the operation itself.

Scope does something similar. PROFINET DCP discovery runs at Layer 2 and never touches TCP/102, so a hunt scoped to that port won’t cover it. That’s not a claim about AA26-231A activity, just a reminder that scoping a hunt to one port scopes your visibility to one port.

Four Claims Worth Keeping Separate

  • Real-time detection. Can you alert while it is happening? The evidence is the request on the wire, observed as it passes.
  • Threat hunting. Can you find it afterward? The evidence is retained packet and protocol metadata you can query across the window.
  • Incident validation. Did the requested operation succeed? The evidence is the matching response, checked at the function and item level rather than at the top-level header.
  • Process-impact validation. Did the physical process change? The evidence is controller state, historian trends, SCADA alarms, and operator observation.

A PLC Stop request on the wire tells you a request was sent. A response with no top-level protocol error may still need function-specific and item-level return values checked before you can call the operation complete. And protocol success tells you nothing about whether a pump, breaker, or line moved. Four claims, four kinds of evidence, and the pressure to collapse them is highest exactly when you can least afford it.

What Holds Up

The advisory is right that this is worth hunting and right to point at behavior instead of stable indicators. The work is all in the implementation.

Protocol semantics tell you what happened on the wire. Operational context tells you whether it mattered. The collection path decides whether your sensor saw it correctly in the first place. All three have to hold, and the capture above is what it looks like when the middle one is missing: a technically correct rule firing thousands of times at a network doing its job.

Library names and file paths will keep changing. The relationships between sources and controllers, the operations that run across them, and whether anyone authorized either will still be there next year.

Our thanks to our customer for supporting this work. Research like this only gets done when organizations and operators are willing to let people look closely at a network that has to keep running.

No noise. Just signal.

~Jori 🤘🔥