Detecting Stuxnet Virus in Siemens S7-300/S7-400 PLCs via SFC51 Checksum Verification
The Stuxnet worm, uncovered on 17 June 2010 after a development period estimated to begin in 2005, was the first publicly identified malware engineered specifically to subvert industrial control systems. While modern Siemens S7 controllers ship with substantially hardened firmware and SIMATIC Security Advisories, legacy S7-300 and S7-400 systems deployed in brownfield environments remain candidates for forensic verification. This reference documents the SFC51 (RDSYSST) SSL checksum technique for detecting unauthorized modifications to the user program — the same class of modification Stuxnet performs by injecting code blocks into OB35.
1. Stuxnet Threat Profile in S7 Environments
Stuxnet is a multi-stage worm that targets Siemens SIMATIC S7-300/S7-400 PLCs through STEP 7 / WinCC / PCS 7 engineering stations. The attack chain is documented in detail by Kaspersky Lab, Symantec, and the IEEE Spectrum retrospective (see The Real Story of Stuxnet — IEEE Spectrum). The PLC payload (commonly attributed to the "831 payload") executes when the following conditions are simultaneously true on the targeted controller:
- CPU family equals S7-300/400 (specifically 6ES7 315-2 and 6ES7 417 families observed in samples)
- Number of frequency converters (drives) connected matches a hard-coded range
- Specific data block numbers and function-block signatures are absent (the worm avoids contaminating programs that contain the user's own instances of the same names)
- WinCC flexible / PCS 7 runtime is present on the engineering host
Because activation is conditional, a benign S7-300 in a non-PLC-Stuxnet configuration may be infected but dormant. The code-checksum delta is the most reliable detection signal when the payload has been installed and is waiting on a target device profile.
2. Stuxnet Injection Targets in S7 User Memory
The Stuxnet PLC payload operates as a block-substitution attack on the CPU's load memory. The following modifications are observed in infected S7-300/400 firmware images:
| Target Block | Stuxnet Modification | Detection Surface |
|---|---|---|
| OB1 (main cyclic) | Inlined hook at scan start | Code checksum drift (SSL W#16#0232) |
| OB35 (cyclic interrupt 100 ms) | Replaced with malicious handler | Code checksum drift; OB35 timebase glitch |
| FB/FC blocks copied as DP_RECV replacements | Impersonates Siemens library FBs | Block-list cross-check against reference project |
| Newly written DBs (e.g., DB8062, DB8063, etc.) | Data-store for the 831 payload | Online block list vs. offline project archive |
The OB35 substitution is the highest-value detection vector because the legitimate OB35 contains the customer's application logic, and any replacement will alter the SSL W#16#0232 hash for that block.
3. SFC51 (RDSYSST) and SSL Fundamentals
SFC51 "RDSYSST" is the System Function Call that reads entries from the CPU's System Status List (SSL). The SSL is a CPU-internal data structure that exposes diagnostic, identification, and checksum information. It is the only legitimate on-line path to obtain the CPU-computed checksums of loaded code blocks.
The block interface in STEP 7 V5.x for S7-300/400:
// SFC51 - RDSYSST - Read system status list
// Function block interface
VAR_INPUT
REQ : BOOL; // Edge-triggered request
SSL_ID : WORD; // System Status List ID
INDEX : WORD; // Sub-index / partial list selector
END_VAR
VAR_OUTPUT
RET_VAL : INT; // Error code (0 = OK, <0 = SFC error)
BUSY : BOOL; // 1 while read in progress
SSL_HEADER : STRUCT // First 4 words of the result
LENGTH : WORD; // Total length of returned data in bytes
N_DR : WORD; // Number of data records in this list
END_STRUCT;
END_VAR
VAR_IN_OUT
DR : ANY; // Destination area (must be at least 34 bytes)
END_VAR
SSL_HEADER.LENGTH against your buffer to avoid CPU STOP on an out-of-range access.4. SSL_ID W#16#0232 — Module Identification & Checksum
SSL partial list W#16#0232 is the documented entry for module identification and the rolling checksum of the loaded code blocks. The parameter mapping is:
| Parameter | Value | Meaning |
|---|---|---|
SSL_ID |
W#16#0232 | Identification and checksum partial list |
INDEX |
W#16#0004 | Sub-index requesting checksum data record |
| Result length (S7-300) | Variable | Depends on CPU type and load memory contents |
| Result length (S7-400 / S7-400H) | 80 bytes | Includes module type ID + 2 × CRC32 words |
The checksum returned is not a single hash but a CPU-computed rolling CRC over the active code blocks. Any block added, removed, or modified by an operator download — or by Stuxnet — changes the value.
5. STEP 7 Implementation Example
The following Structured Text fragment, valid in STEP 7 V5.5 / S7-SCL, demonstrates a one-shot read of SSL W#16#0232 / W#16#0004 into a marker area, with error handling for the documented SFC51 fault codes.
// SCL — One-shot SSL checksum read
// Place in OB100 (startup) or OB1 edge-triggered
FUNCTION_BLOCK FB_SSL_Check
VAR
sfc51_executed : BOOL := FALSE;
ssl_done : BOOL := FALSE;
ssl_error : INT := 0;
ssl_buffer : ARRAY[0..99] OF BYTE; // Over-allocated for safety
next_check : DWORD;
END_VAR
BEGIN
IF NOT sfc51_executed THEN
sfc51_executed := TRUE;
// OB1 call — call only on rising edge of REQ
RDSYSST(
REQ := TRUE,
SSL_ID := W#16#0232,
INDEX := W#16#0004,
RET_VAL := ssl_error,
BUSY := ssl_done,
DR := ssl_buffer
);
END_IF;
// Persistent storage for SCADA / HMI comparison
IF ssl_error = 0 THEN
// Copy the first 8 bytes (module type + 4-byte CRC) to a known DB
// for the HMI to compare against the gold image
END_IF;
END_FUNCTION_BLOCK
Pair this block with an online-to-offline diff using STEP 7's PLC > Compare Blocks function, which performs the same CRC calculation and reports block-level deltas that the SSL read cannot resolve.
6. SFC51 Error Code 8082h Diagnostics
A common operator-side failure is the return value 32898 decimal = 8082h when calling SFC51 with the wrong SSL_ID. The full matrix of SFC51 return codes is documented in the STEP 7 online help under "SFC51 — RDSYSST":
| RET_VAL (hex) | RET_VAL (dec) | Cause | Remediation |
|---|---|---|---|
| 0000h | 0 | No error | Proceed with diff |
| 7000h | 28672 | BUSY — call in progress | Re-read after BUSY=0 |
| 8081h | 32897 | DR target area too small | Enlarge buffer per SSL_HEADER.LENGTH |
| 8082h | 32898 | SSL_ID not supported on this CPU | Verify CPU supports W#16#0232; check firmware |
| 8083h | 32899 | INDEX not valid for the SSL_ID | Use W#16#0004 with W#16#0232 |
| 8084h | 32900 | Reading not possible (RESET/STOP state) | Re-attempt in RUN |
| 80B1h | 32945 | No valid diagnostic data | Hardware fault — check diagnostic buffer first |
W#16#0232 in SCL/ST, not the BCD-style B#16#232 which compiles on some firmware revisions and silently produces the wrong identifier on others.7. Stuxnet Detection Procedure — Step-by-Step
The following procedure isolates Stuxnet-style user-program tampering on a live S7-300/400 CPU. It is intended to be run from a clean engineering station with a known-good offline project archive.
-
Establish a trusted baseline. Use the offline project archive of the controller that was last verified clean. Export an Online Block List via STEP 7 and store the SHA-256 fingerprints of the offline
.s7parchive. - Open the project in STEP 7 V5.5+. From the menu select PLC > Compare Blocks (Online vs. Offline). The tool performs the same CRC that SSL W#16#0232 exposes, and additionally reports per-block differences. Any delta on OB1, OB35, OB100, OB80–OB87, or any DP_RECV/DP_SEND is a positive finding.
- Call SFC51 with SSL_ID = W#16#0232 / INDEX = W#16#0004 from a temporary FB. Capture the 80-byte result into a non-volatile DB and copy it via the engineering station for archive comparison.
-
Cross-check the block list against known Stuxnet artifacts. Investigate any unexpected DBs in the high-number range (DB8062 and similar observed in Stuxnet samples — exact numbers vary by variant). Also check for FBs named to mimic Siemens library primitives (e.g.,
FC1865/FC1883imitating DP_RECV). - Inspect the diagnostic buffer (SSL W#16#00A0) for "Mode transition to STOP caused by OB35 processing error" or unauthorized priority-class changes — both have been observed in published Stuxnet analyses.
- Sanitize the engineering host. Before any re-download, run a clean WinCC / STEP 7 host with updated antivirus signatures and offline copies of the project. The Stuxnet dropper persists on the engineering workstation and re-injects the PLC payload after any download from an infected host.
- Re-flash the CPU's load memory via STEP 7 PLC > Download User Program to Memory Card from the clean archive, and verify the SSL checksum matches the recorded baseline.
8. Stuxnet's Checksum-Evasion Mechanism
Field analyses reported in the security research community have shown that the Stuxnet PLC payload contains its own DP layer stub that hooks the responses to block-read and SSL-read requests. When a STEP 7 host requests the checksum or the contents of a contaminated block, the payload substitutes the original (pre-infection) bytes. This means:
- Online block-view in STEP 7 will show the un-infected OB35, even when the live execution copy is malicious.
- SSL W#16#0232 may return the pre-infection checksum, depending on the variant and the exact hook path.
- Only a physical extraction of the load memory (via MMC reader for S7-300C, or PG-level card pull for S7-400) and a SHA-256 comparison against a known-good binary guarantees detection.
dd on a Linux host, and compare the binary against the offline project archive's compiled .s7f images.9. Complementary Detection Indicators
Because the SSL checksum can be spoofed, layer the following passive signals onto a SCADA historian or WinCC alarm log:
- OB35 cycle-time drift: Log the OB35 execution time. Stuxnet's payload adds measurable processing load; deviations >5% of baseline are significant.
- Unknown DBs in the on-line block list (especially DBs in the 8000+ range that are not in the offline project).
- Diagnostic buffer entries for unauthorized STOP / RUN transitions, OB85 (priority class error), or OB121 (programming error) clustering on OB35.
- Engineering-host network traffic to the PLC on PROFIBUS / MPI from sources other than the known engineering station (Stuxnet propagates laterally between controllers on the same DP segment).
10. Hardening Recommendations for S7-300/S7-400 Sites
- Apply Siemens Security Advisory SSA-180145 and successor advisories for the installed CPU firmware. Newer firmware revisions tighten the DP response path and reduce the surface for substitution hooks.
- Disable unused cyclic OBs (OB30–OB38) in HW Config where the application does not use them. Even if the CPU is infected, the malicious handler cannot run if the OB is administratively disabled.
- Block USB mass-storage on engineering hosts via Windows Group Policy and disable the SIMATIC WinCC/Step7 USB automation handler. Stuxnet's primary vector is removable media on engineering stations.
- Restrict PROFIBUS / Ethernet write access to the PLC via the Access Protection (CPU password) and Connection Configuration in STEP 7. Default "No Password" allows any node on the segment to download.
- Establish a monthly SSL W#16#0232 read-and-archive procedure; store the 80-byte snapshot in a write-once location (e.g., a SFTP-mounted archive). This is your long-term forensic baseline.
- Segment the OT network: place the engineering VLAN behind a stateful firewall that restricts S7comm (port 102) to known engineering hosts only. The original Stuxnet propagation is impossible if lateral S7comm is blocked.
11. Verification and Validation
After a remediation event, the following checks confirm a clean state:
- SFC51 with SSL_ID W#16#0232 / INDEX W#16#0004 returns
RET_VAL = 0and the 80-byte buffer matches the SHA-256 hash of the clean archive. - STEP 7 Compare Blocks (Online vs. Offline) reports no differences.
- OB35 cycle time returns to baseline within ±2%.
- The diagnostic buffer shows no unauthorized STOP/RUN transitions or OB85 events from the previous 30 days.
- The on-line block list contains exactly the blocks present in the offline project, and no DBs in the >8000 range unless explicitly documented.
If any of the above fails, treat the system as compromised, perform a full MMC re-image from a clean source, and reset all engineering-host credentials.
FAQ
What SSL_ID and INDEX should I use to read the S7-300/S7-400 user-program checksum?
Use SSL_ID = W#16#0232 with INDEX = W#16#0004 in a SFC51 (RDSYSST) call. On S7-400 / S7-400H this returns 80 bytes of module identification plus the rolling CRC over the loaded code blocks. The destination buffer must be at least 80 bytes (recommend 100 bytes) to avoid the 8081h error.
What does SFC51 return value 32898 (8082h) mean?
8082h means the SSL_ID is not supported on the current CPU type or the wrong literal format was used. Verify that you pass W#16#0232 (a WORD literal) rather than B#16#232 (a BYTE literal), and confirm the CPU firmware supports the identification-and-checksum partial list. The full SFC51 error matrix is in the STEP 7 online help under SFC51 — RDSYSST.
Can Stuxnet hide its modifications from the SSL checksum read?
Yes. Documented variants of the Stuxnet PLC payload hook PROFIBUS / S7comm responses and substitute the pre-infection block content and checksum. If a discrepancy is suspected, pull the memory card, image it with a Linux dd command, and compare the binary directly against a known-good offline project archive. Do not rely on the live online view alone.
Does Stuxnet activate on every Siemens S7 PLC?
No. Activation is conditional on a hard-coded profile of CPU type, presence of specific frequency converters, and absence of certain user-defined function blocks. Many S7-300/400 systems in the wild can be infected but remain dormant indefinitely. The code-checksum delta from SFC51 still indicates the dormant payload's presence.
What is the most reliable Stuxnet detection method for an S7-400H redundant pair?
Combine three checks: (1) SFC51 SSL W#16#0232 read on both H-CPUs and compare against the offline project SHA-256; (2) STEP 7 Compare Blocks (Online vs. Offline) on each H-CPU; (3) physical extraction of the sync-module / MMC images and a binary diff. SSL-only detection can be spoofed by the payload's DP-response hook.
Where can I find Siemens' official advisory on Stuxnet?
Siemens published Security Advisory SSA-180145 and updates on the Siemens ProductCERT portal. For the historical technical analysis, see the Stuxnet Wikipedia entry and the IEEE Spectrum retrospective. Always cross-reference current advisories on the official Siemens Industrial Security site when remediating active systems.