
Public technical advisory and reproduction evidence for CVE-2026-52134 affecting GOOSE replay handling in libiec61850 v1.6.
CVE-2026-52134 has been assigned. Publication of the corresponding CVE Record is pending.
src/goose/goose_receiver.cparseGoosePayload()The GOOSE subscriber receive path in libiec61850 v1.6 does not adequately reject certain replayed or stale GOOSE messages before updating subscriber-visible state and invoking the registered listener callback.
Two controlled experiments demonstrate related behavior in the same receive path:
stNum rollback: after the subscriber processed stNum=2, replaying a previously captured frame caused the listener to report the older state and data again.stNum=1sqNum replay: when a previously captured frame with the same stNum but an older sqNum was replayed, the frame was marked invalid, but listener callbacks still occurred and replayed data remained visible.These are presented as two related observations of one replay/message-freshness handling issue, not as two separate CVEs.
An attacker must be able to:
0x88B8This is not an arbitrary Internet-based remote attack.
The relevant receive logic checks sqNum when the received stNum equals the stored stNum. The tested code path does not reject a lower stNum before subscriber state is updated and the listener is invoked.
The tested library file was not modified. The original and working copies of src/goose/goose_receiver.c had the same SHA-256 value:
c42f2aec80f605c458d0bca524ccb09f2e35f9b83a02f854f95d9444616c075b

veth0 / veth1 isolated virtual Ethernet pairtcpdumpThe test harness produced controlled state transitions and printed callback-visible stNum, sqNum, validity, and data values. The vulnerable library file itself was not modified.
The controlled publisher sent two states:
State A: stNum=1, sqNum=0, data=1111
State B: stNum=2, sqNum=0, data=2222
The baseline packet capture contained two GOOSE frames in this order:
Frame 1: stNum=1, sqNum=0
Frame 2: stNum=2, sqNum=0

The subscriber output also contained an auxiliary callback for stNum=1, sqNum=0 marked valid=false before State B. This auxiliary callback is retained in the evidence but is not used as the basis for the rollback conclusion. The decisive pre-replay state was the later callback containing stNum=2 and data value 2222.
The replay script loaded the two-frame baseline capture, selected frame 1, and sent it once through veth0.
Immediately before the replay, the listener had reported:
stNum=2, sqNum=0, valid=true, allData={2222}
After the old frame was replayed once, the listener reported:
stNum=1, sqNum=0, valid=true, allData={1111}

This demonstrates an observed subscriber-state rollback from stNum=2 to stNum=1 after replaying a previously captured lower-stNum frame.
Four more copies of the same old frame were sent. All four contained stNum=1, sqNum=0.
The later copies were reported as valid=false, but callback numbers continued to increase and the replayed data remained visible:

This secondary observation shows that marking a repeated frame invalid did not prevent listener delivery in the tested receive path.
An earlier reproduction used the official example publisher and subscriber. The publisher produced a normal sequence with:
stNum=1
sqNum=0, 1, 2, 3
The first captured frame (stNum=1, sqNum=0) was then replayed after the subscriber had already processed the later sequence value.

The replayed copies were reported as invalid because the received sqNum=0 was not newer than the stored sequence value. However, the subscriber continued to print listener events containing the replayed data:

This experiment supports a narrower but related observation:
The raw original replay screenshots are retained as supporting material:
Those raw images contain unrelated Scapy optional-module import warnings. The warnings did not prevent the script from reporting the captured GOOSE frames and completing transmission, but the cleaner screenshots above are preferred for reviewing the technical result.
The experiments demonstrate different branches of the same GOOSE replay/message-freshness problem:
| Observation | Received message | Observed result |
|---|---|---|
Lower-stNum replay | Stored stNum=2; received old stNum=1 | Old state and data were delivered to the listener and reported as valid in the tested run |
Non-increasing sqNum replay | Same stNum; received older or duplicate sqNum | Message was reported invalid, but listener callbacks and replayed data delivery continued |
The first observation is the primary CVE finding because it demonstrates rollback from a newer state to an older state. The second observation is supporting evidence about how invalid same-state replays continue through the listener path.
The demonstrated software-level impact is:
stNum to an older stNum.Applications that consume callback data without independent freshness enforcement may process stale or replayed values.
The downstream effect depends on the subscriber application, configuration, interlocking logic, and protection logic.
No physical protection relay, trip circuit, circuit breaker, or production substation network was tested or operated in these experiments.
Before updating subscriber state or invoking the listener:
stNum that is older than the last accepted stNum.stNum is unchanged, reject a non-increasing sqNum.stNum rollback and repeated sqNum values.The following images provide provenance and environment information. They support the reproduction but are not individually required to understand the main result.





The demonstrated result is replay acceptance and stale-state delivery. It does not depend on proving that cryptographic authentication was enabled in the tested deployment.
Reported by: