When the Same Bug Came Back Four Times: The libIEC61850 and lib60870 Parser Advisories Through an IEC 62443-4-1 and IEC 61850-5 Lens
Two CISA advisories dropped on July 23, 2026, covering two libraries from the same maintainer. Read individually, they are ordinary: patch, move on. Read against the four-year record of the same codebase, they are something else. The stack buffer overflow disclosed in 2022, the buffer overflows disclosed in 2024, and the heap overflow disclosed last week are not four bugs. They are one unwritten requirement, failing four times, at four different layers of the same decode path. The interesting question is not why the bug exists. It is why the fix keeps being a patch instead of a verification activity.
On July 23, 2026, CISA published ICSA-26-204-06 covering MZ Automation's libIEC61850, and ICSA-26-204-07 covering the same maintainer's lib60870. libIEC61850 is the open-source C implementation of IEC 61850 — MMS, GOOSE, Sampled Values, file services, TLS — that a great many substation IEDs, gateways, engineering workstations and test tools are built on. lib60870 is its sibling for IEC 60870-5-101 and -104, the telecontrol protocols that carry RTU telemetry in substations, water plants and pipelines. Between them they sit in the Energy, Critical Manufacturing, Transportation Systems, Chemical, and Water and Wastewater sectors, deployed worldwide.
Neither advisory reports known exploitation. Both are disclosures. And both land on the same defect class, at the same architectural position, that this codebase has been patching since 2022.
The public record
libIEC61850 (ICSA-26-204-06). Versions v1.0.0 through v1.6.1 are affected — which is to say, every release the library has ever shipped. The advisory covers four weakness classes: a stack-based buffer overflow reachable via a ReadRequest, a heap-based buffer overflow reachable via a crafted MMS Initiate request, improper handling of syntactically invalid structures, and a NULL pointer dereference. An unauthenticated, network-adjacent attacker can crash IEC 61850 services or, in the worst case, execute code.
The heap overflow is tracked as CVE-2026-49035 and carries a CVSS v4.0 base score of 9.2 (Critical), vector AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H, and a CVSS v3.1 score of 8.1. The CVE description is unusually candid about exploitability: "Remote code execution has been demonstrated when ASLR is disabled; memory corruption or denial of service may occur in configurations where ASLR is enabled." Hold that sentence. Embedded IEDs are precisely the class of target where ASLR is frequently absent, and the caveat is doing a lot of work.
MZ Automation released libIEC61850 1.6.2 the same day. The changelog is the real story. It is not a single patch — it is a sweep across seven protocol-processing boundaries at once: out-of-bounds read protection during MMS connection setup and IEC 61850 object-reference handling, NULL dereference guards in both server and client paths, validation of malformed responses that could corrupt the heap, length checks added to R-Session, ISO Presentation and ISO Session parsing, hardening of COTP option parsing, integer-overflow fixes in the BER decoder, prevention of unbounded recursion when processing GOOSE data, and enforcement of buffer limits during message-size handling.
That is not the profile of a bug fix. That is the profile of a codebase discovering it never had an input-validation invariant in the first place.
lib60870 (ICSA-26-204-07). Versions up to and including 2.4.0 are affected by CVE-2026-16002, a CWE-125 out-of-bounds read, CVSS v3.1 8.2 and CVSS v4.0 8.8, reported by researcher Lars Tray. The trigger, per the advisory, is the parsing of ASDU Type ID 41 — S_IT_TC_1, the integrated-total-of-security-statistic object with a time tag. When a frame declares more data elements than it actually contains, the parser validates only part of the required object length before going on to read a larger block that includes the counter data and the timestamp. The result is a heap over-read and a process crash. The path is reachable from CS101 master and CS104 client code, meaning any application that terminates IEC 60870-5-104 on TCP port 2404 and handles Type 41 objects can be dropped by one packet. Fixed in 2.4.1.
Sit with the irony for a second. S_IT_TC_1 is not a legacy telecontrol object. It is one of the security ASDUs introduced by IEC 60870-5-7 and IEC 62351-5 — the objects that exist to report security statistics. The parser for the security-monitoring message is the one that hands an attacker a denial of service.
And now the part that matters. This is the fourth public round on the libIEC61850 decode path in four years:
- 2022 — Claroty Team82 (Mashav Sapir, Vera Mens) fuzzed
MmsServerConnection_parseMessagewith AFL and disclosed four CVEs, two of them CVSS v3 10.0. The clearest, CVE-2022-2972, is a fixedchar continueAfterIdMemory[130]on the stack,memcpy'd with acontinueAfterLengthtaken straight fromBerDecoder_decodeLengthon the wire, inmmsServer_handleGetNameListRequest. Attacker controls both the size and the contents of the overflow. Team82's write-up notes that on embedded targets built without ASLR or stack canaries, building a ROP chain from that primitive is "trivial." CISA published ICSA-22-251-01. - 2024 — ENCS testers Humza Ahmad and Albert Spruyt disclosed three more in the MMS client: CVE-2024-45969, a NULL dereference triggered by a crafted
InitiationResponsefrom a malicious server, and CVE-2024-45970 and CVE-2024-45971, buffer overflows in the parsing ofFileDirResponseandIdentifyResponse. All fixed in 1.6.0. - 2024–2025 maintenance releases — the 1.6.0 and 1.6.1 changelogs quietly list more of the same, self-reported: out-of-bounds reads in the ACSE
parseAarqPduandparseAarePdufunctions, added length and plausibility checks in the GOOSE receiver, an off-by-one in the parsing ofservicesSupportedin the MMS init response, a BER decoder infinite-length handler that could spin forever, and a buffer underflow inIedServer_getFunctionalConstrainedData. - 2026 — ICSA-26-204-06, and the seven-boundary sweep in 1.6.2.
Different reporters, different fuzzers, different years, different protocol layers. Identical defect class, identical architectural position: the point where an attacker-supplied length field is trusted before it is bounded.
The standards lens
The correct lens here is IEC 62443, not a safety standard — these are security defects in a communications component. But the safety consequence is one hop away, and I will get to that.
IEC 62443-4-2, component requirements — CR 3.5 and CR 7.2. CR 3.5, Input Validation, requires a component to validate the syntax and content of any input received on a network interface. Every one of the CVEs above is a CR 3.5 failure, and the recurrence tells you the requirement was never decomposed to the level where it could be verified. "Validate input" is not a testable requirement. "Every decode function shall verify that a declared length does not exceed the bytes remaining in the frame before performing any read" is. The second one can be enforced by a static-analysis rule and a fuzz gate. The first one can only be enforced by hoping. CR 7.2, Resource Management, is the requirement the unbounded-GOOSE-recursion fix in 1.6.2 belongs to.
IEC 62443-4-1, the secure development lifecycle — SVV-3 and DM-4. This is the clause that should have ended the story in 2022. SVV-3, Vulnerability Testing, explicitly names fuzz testing of protocol interfaces as a required verification activity for a supplier claiming 4-1 conformance. Team82 demonstrated in 2022 that a single AFL harness on one entry point (MmsServerConnection_parseMessage) found four vulnerabilities in a few days of fuzzing. ENCS repeated the exercise against the client side in 2024 and found three more. Both were external parties doing, on their own time, the verification activity SVV-3 defines as the supplier's job — and each pass found bugs because each pass pointed at a different entry point.
DM-4, Addressing Security-Related Issues, is the other half. 62443-4-1 defect management does not stop at "fix the reported instance." It requires the supplier to determine the root cause and address related issues — the class, not the instance. Four rounds of the same CWE at four different layers is the textbook signature of instance-level remediation. In fairness, the 1.6.2 changelog looks like the first time the class was attacked directly: seven boundaries hardened at once, including layers nobody had reported a CVE against yet. That is the right move. It is four years late, and it is being made by a small maintainer carrying an enormous installed base.
IEC 62443-4-1 SM-9/SM-10 and CR 7.8 — the supply chain. Neither library is the product. Both are inside products: IEDs, protocol gateways, data concentrators, historian collectors, engineering workstations, commissioning laptops. They are usually statically linked, which means no libiec61850.dll on disk and nothing for a vulnerability scanner to find. If you are an asset owner, "am I affected" is not answerable by scanning. It is answerable only by an SBOM, which is what CR 7.8 (component inventory) and SM-9 (security requirements for externally provided components) exist to force. This is also the clock the EU Cyber Resilience Act starts running: manufacturer reporting obligations for actively exploited vulnerabilities in products with digital elements begin in September 2026, roughly six weeks from this advisory, and the CRA's open-source steward provisions are aimed squarely at exactly this shape of dependency.
IEC 61850-5 and IEC 62351 — where the security defect becomes an availability problem. IEC 61850-5 assigns Type 1A "Fast Messages" — the trip and block signals exchanged between IEDs over GOOSE — a total transfer time budget of 3 ms for performance classes P2 and P3. GOOSE is Layer 2, publish-subscribe, no TCP, no TLS. The 1.6.2 fix for unbounded recursion in GOOSE data processing is therefore the most operationally serious item in the whole changelog, and it did not get its own CVE. A subscriber that can be crashed or stack-exhausted by a malformed GOOSE frame is a subscriber that has lost the protection signal path — and unlike a SCADA session, there is nothing to time out and retry.
On the MMS side, IEC 62351-3 gives you TLS on the 61850 client-server association, and 62351-4 adds application-layer security. It is worth being precise about what that buys you: TLS terminates before MMS decode, so it restricts who can speak to the parser. It does nothing about a compromised or misconfigured peer that is already inside the trust boundary, and it does nothing at all for GOOSE. Encryption moves the attack surface. It does not remove it.
The safety hop. libIEC61850 and lib60870 are not safety-rated elements, and nobody claims they are. But if an application built on them is a component of a safety instrumented system or a protection scheme, then IEC 61508-3 Clause 7.4.2.12 (previously developed software) and IEC 61511-1 Clause 11.5 apply to the element, and a library with seven parsing weaknesses fixed in one release does not have a systematic-capability argument you would want to defend. IEC 61511-1 Clause 8.2.4 requires a security risk assessment of the SIS. This advisory is what that assessment is supposed to catch before an advisory does.
A worked snippet: the defect, mapped to the decode stack
The reason this recurs is visible the moment you draw the decode path. IEC 61850 MMS is a 1990s OSI application protocol tunnelled over TCP via RFC 1006. That means seven independent parsing layers, each with its own length fields, each written by hand in C, each an independent opportunity to trust a number that came off the wire.
Untrusted frame (station bus / control-centre WAN / TCP 102)
|
+-----v----------------------+
| L1 TPKT (RFC 1006) | 4-byte hdr, 16-bit length
+----------------------------+
| L2 COTP (ISO 8073) | option parsing .......... hardened 1.6.2
+----------------------------+
| L3 ISO Session (ISO 8327) | SPDU length checks ...... hardened 1.6.2
+----------------------------+
| L4 ISO Presentation(8823) | ASN.1 / BER lengths ..... hardened 1.6.2
| | BER integer overflow .... hardened 1.6.2
| | BER infinite-length loop . fixed 1.6.1
+----------------------------+
| L5 ACSE (ISO 8650) | parseAarqPdu OOB read ... fixed 1.6.0
| | parseAarePdu OOB read ... fixed 1.6.0
+----------------------------+
| L6 MMS (ISO 9506) | GetNameList stack o/f .. CVE-2022-2972
| | Identify overflow ... CVE-2024-45971
| | FileDirResp overflow ... CVE-2024-45970
| | InitiateResp NULL deref . CVE-2024-45969
| | Initiate heap o/f ... CVE-2026-49035
| | ReadRequest stack o/f .. ICSA-26-204-06
+----------------------------+
| L7 IEC 61850 object layer | object-ref OOB read ..... hardened 1.6.2
+----------------------------+
|
GOOSE (L2, separate path) | unbounded recursion ..... hardened 1.6.2
Every entry in that column is the same failure written in a different function. Here is the 2022 instance in full, because it is the clearest statement of the invariant nobody wrote down:
/* mmsServer_handleGetNameListRequest — libIEC61850, pre-1.6.0 */
char continueAfterIdMemory[130]; /* fixed-size stack buffer */
char* continueAfterId = NULL;
if (continueAfter != NULL) {
continueAfterId = continueAfterIdMemory;
memcpy(continueAfterId, continueAfter, continueAfterLength); /* (1) */
continueAfterId[continueAfterLength] = 0; /* (2) */
}
/* continueAfterLength comes from:
BerDecoder_decodeLength(buffer, &continueAfterLength, bufPos, maxBufPos);
i.e. straight off the wire. No comparison against 130 anywhere.
(1) overflows the stack buffer; (2) writes one byte past the overflow. */
And the recurrence, tabulated. Same column, four times:
| Year | Reporter | Identifier | CWE | Decode layer / trigger | Fixed in |
| --- | --- | --- | --- | --- | --- |
| 2022 | Claroty Team82 | CVE-2022-2970, -2972 (CVSS 10.0) | CWE-121 stack overflow | L6 MMS — GetNameList continueAfter | 1.5.x |
| 2022 | Claroty Team82 | CVE-2022-2971, -2973 | CWE-843, CWE-476 | L6 MMS — type confusion, NULL deref | 1.5.x |
| 2024 | ENCS | CVE-2024-45970, -45971 | CWE-787 buffer overflow | L6 MMS client — FileDirResponse, IdentifyResponse | 1.6.0 |
| 2024 | ENCS | CVE-2024-45969 | CWE-476 NULL deref | L6 MMS client — InitiationResponse | 1.6.0 |
| 2024 | Maintainer | (no CVE) | CWE-125 OOB read | L5 ACSE — parseAarqPdu, parseAarePdu | 1.6.0 |
| 2025 | Maintainer | (no CVE) | CWE-835 infinite loop | L4 BER — indefinite-length handling | 1.6.1 |
| 2026 | Undisclosed | CVE-2026-49035 (CVSS v4 9.2) | CWE-122 heap overflow | L6 MMS — Initiate request | 1.6.2 |
| 2026 | Undisclosed | ICSA-26-204-06 | CWE-121 stack overflow | L6 MMS — ReadRequest | 1.6.2 |
| 2026 | Lars Tray | CVE-2026-16002 (CVSS v4 8.8) | CWE-125 OOB read | lib60870 ASDU 41 S_IT_TC_1 | 2.4.1 |
Read down the CWE column. Five distinct weakness identifiers, one root cause: a length taken from an untrusted frame is used before it is bounded against the frame. Read down the "Fixed in" column and you see instance remediation — each release closes the reported functions and leaves the adjacent ones for the next researcher.
Here is the same finding expressed as an IEC 62443-3-2 zone-and-conduit risk row for an asset owner rather than the supplier:
| Field | Value | | --- | --- | | Zone | Substation station bus (SL-T 2) | | Conduit | Control centre to gateway, IEC 60870-5-104 / TCP 2404; station bus MMS / TCP 102 | | Threat | Malformed protocol frame from a compromised peer, spoofed source, or misconfigured vendor remote-access path | | Vulnerability | CVE-2026-49035 (heap o/f, MMS Initiate); CVE-2026-16002 (OOB read, ASDU 41) | | Unmitigated consequence | Loss of view and loss of control on the affected gateway; potential code execution on non-ASLR IED targets | | Unmitigated likelihood | Medium — network-adjacent, no authentication required, EPSS currently near zero | | Existing countermeasures | Firewalled conduit; IEC 62351-3 TLS on the MMS association where supported | | Residual gap | GOOSE path unauthenticated and unencrypted by design; TLS terminates before the vulnerable decode; library version unknown for 60% of installed IEDs (no SBOM) | | Required SL-C | SL 2, requiring CR 3.5 and CR 7.2 to be met by the component |
That last row is the one that should be uncomfortable. You cannot claim SL-C 2 for a component whose supplier cannot evidence SVV-3 vulnerability testing across every decode entry point.
Derived requirements (excerpt)
These are written for a supplier shipping either library inside a product. IDs are stable.
-
SEC-PARSE-001 (Length-before-read invariant). Every decode function that consumes a length field originating from a network frame shall verify that the declared length does not exceed
maxBufPos - bufPosbefore performing any read,memcpy, or index operation, and shall return a decode error otherwise. No fixed-size stack buffer shall be the destination of a copy whose length derives from network input. Verification: static-analysis rules enforcing CERT C ARR30-C and INT31-C across every decode translation unit, zero violations, build-gating. Traces to: IEC 62443-4-2 CR 3.5; the entire 2022–2026 CVE column. -
SEC-PARSE-002 (Per-layer fuzz gate). Each of the seven decode entry points — TPKT, COTP, ISO Session, ISO Presentation, ACSE, MMS PDU, IEC 61850 object reference — plus the GOOSE subscriber and the IEC 60870-5 ASDU decoder shall have a dedicated coverage-guided fuzz harness. Release gate: a minimum of 72 CPU-hours per harness under AddressSanitizer and UndefinedBehaviorSanitizer with zero new unique crashes, and edge coverage of the target translation units at or above 85%. Traces to: IEC 62443-4-1 SVV-3. Rationale: both the 2022 and 2024 disclosures came from a single external harness on a single entry point. Nine harnesses is the difference between finding these in CI and finding them in a CISA advisory.
-
SEC-PARSE-003 (Recursion and resource bound). Protocol decoders shall be non-recursive, or shall enforce a compile-time nesting bound of 8 levels with an explicit error return on exceedance. Per-connection heap allocation shall be capped at the negotiated MMS PDU size and shall not grow unbounded across a session. Verification: fault injection with deeply nested BER structures and oversized GOOSE datasets; peak RSS per connection measured and bounded. Traces to: IEC 62443-4-2 CR 7.2; the 1.6.2 GOOSE recursion fix.
-
SEC-PARSE-004 (Protection-path independence and containment). No IEC 61850-5 Type 1A function shall depend on the MMS or IEC 60870-5-104 stack. A GOOSE subscriber decode fault shall be contained to the offending frame: discard, increment a diagnostic counter, publish a quality flag, and continue. Verification: inject malformed GOOSE frames at 1 000 frames per second for 60 minutes while measuring end-to-end trip transfer time; the 3 ms Type 1A P2/P3 budget shall be met at the 99.9th percentile with zero subscriber restarts. Traces to: IEC 61850-5 performance classes; IEC 62443-4-2 CR 7.1.
-
SEC-PARSE-005 (Provenance and notification SLA). Every product shipping libIEC61850 or lib60870 shall declare the exact library version and source commit in a CycloneDX or SPDX SBOM delivered with each release, and the supplier shall commit to notifying affected customers within 5 business days of a confirmed upstream advisory. Asset owners subject to NERC CIP-007-6 R2.2 shall be able to complete their 35-day patch assessment from the SBOM alone, without reverse-engineering a binary. Traces to: IEC 62443-4-1 SM-9 and SM-10; IEC 62443-4-2 CR 7.8; EU CRA manufacturer obligations.
Trace note: 001 and 002 close the defect class. 003 closes the resource-exhaustion variant the class produces at the GOOSE layer. 004 keeps the protection function out of the blast radius while 001–003 are being implemented. 005 is what makes any of it actionable for the people who own the substation rather than the code.
What the headline really tells us
The headline is "two CISA advisories, patch your protocol libraries." The engineering reality is that a defect class survived four independent discovery events across four years because nobody ever converted it into a requirement with a number attached and a test that could fail.
That is the missing artifact. Not a patch — there have been plenty of patches, and 1.6.2 is a genuinely good one. What was missing is the line in a verification plan that says every decode entry point gets a fuzz harness, and the build does not ship until every harness runs clean. IEC 62443-4-1 SVV-3 is that line. It has existed since 2018. The reason external researchers kept finding these bugs is not that they were clever; Team82 said as much, describing the ROP chain as trivial. It is that they were the only ones running the test.
There is a fairness point to make here too. MZ Automation maintains two open-source libraries that a substantial fraction of the world's substation automation runs on, and it responded to this round by hardening seven boundaries at once rather than patching the two that were reported. That is the correct engineering response, and it is more than many commercial suppliers manage. The problem is structural: the installed base has commercial-critical-infrastructure expectations and an open-source maintenance budget, and IEC 62443-4-1 conformance costs real money that nobody in that chain is currently paying. The CRA's open-source steward provisions are an attempt to name that gap. Whether they close it is a different question.
For the asset owner, the takeaway is narrower and more immediate. You almost certainly cannot answer "which of my IEDs runs libIEC61850 1.6.1" today, because it is statically linked and your scanner cannot see it. Answering that question is not a patching activity. It is an inventory activity, and it is the one that has to happen first — every time, for every embedded library, forever. The advisory gave you a reason to go ask your vendors. The silence you get back is the finding.
— Jherrod Thomas, The Lion of Functional Safety™
Sources
- CISA — ICS Advisory ICSA-26-204-06, MZ Automation libIEC61850 (July 23, 2026)
- CISA — ICS Advisory ICSA-26-204-07, MZ Automation lib60870 (July 23, 2026)
- Rapid7 Vulnerability Database — CVE-2026-49035, heap-based buffer overflow via crafted MMS Initiate request (CVSS v4.0 9.2 Critical; published July 23, 2026)
- Tenable — CVE-2026-16002, CWE-125 out-of-bounds read in lib60870 (CVSS v3.1 8.2 / v4.0 8.8; published July 23, 2026)
- GitHub — mz-automation/libiec61850 release notes (1.6.0 and 1.6.1 vulnerability fix lists: ACSE parseAarqPdu/parseAarePdu out-of-bounds reads, GOOSE length checks, BER indefinite-length loop)
- Claroty Team82 — "MMS Under the Microscope: Examining the Security of a Power Automation Standard," Mashav Sapir and Vera Mens (October 8, 2024); source of the CVE-2022-2972
continueAfterIdMemory[130]analysis - ENCS — "Critical Security Vulnerabilities Discovered in MZ Automation's MMS Client," CVE-2024-45969/-45970/-45971, testers Humza Ahmad and Albert Spruyt (November 13, 2024)
- CISA — ICS Advisory ICSA-22-251-01, MZ Automation libIEC61850 (the 2022 round)
- The Hacker News — "Researchers Uncover Major Security Vulnerabilities in Industrial MMS Protocol Libraries" (October 2024)
- CFSN Detailed Analysis (Patrick Coyle) — "7 Advisories Published, 7-23-26," confirming the MZ Automation pair in the July 23 batch