high

CVE-2026-113729

Go · github.com/moov-io/signedxml

Summary

Moov signedxml: Unauthenticated denial of service in exclusive XML canonicalization

Severity
high
CVSS
7.5
EPSS
0.5% (p40)
CWE
CWE-400, CWE-407, CWE-770
Also known as
GHSA-5588-rp99-5fjh
Published
2026-10-21
Updated
2026-10-21

Advisory details

Exclusive XML canonicalization (http://www.w3.org/2001/10/xml-exc-c14n#) in github.com/moov-io/signedxml copied the entire accumulated namespace map at every node during ValidateReferences(), before any reference digest or signature check.

For a chain of nested elements that each declare many unused namespace prefixes, that produced O(m × depth²) time and peak memory (m = prefixes per level). beevik/etree caps tree depth (~1024), but m is unbounded, so a small attacker-controlled document (on the order of 1 MB) could drive multi-gigabyte allocations and OOM the process. No valid signature, matching certificate, or authentication is required.

This is the library’s core path for SAML / WS-Federation service providers, which canonicalize attacker-supplied XML prior to authentication.

Affected

Remediation

Do not copy the whole namespace map per node. Maintain a single scope and push/pop only the prefixes introduced at each element. Replace linear prefix-in-scope scans with set lookups. Impose a defensive cap on in-scope namespace declarations and return an error rather than allocating unbounded memory.

From @shuji-oh

Thanks for the fix — I re-ran my PoC against this branch and confirmed the memory blowup is gone.

Before (main)

-- Doubling depth each time (m=10 fixed): allocated grows ~4x each time ≈ O(depth^2) --
depth=250  m=10  payload=  0.07MB allocated=     45MB
depth=500  m=10  payload=  0.14MB allocated=    179MB
depth=1000 m=10  payload=  0.27MB allocated=    711MB
-- Increasing m (depth=1000 fixed): allocated scales roughly linearly with m --
depth=1000 m=5   payload=  0.16MB allocated=    377MB
depth=1000 m=100 payload=  2.57MB allocated=   6278MB
After (fix/exc-c14n-namespace-dos)

-- Doubling depth each time (m=10 fixed): allocated grows ~4x each time ≈ O(depth^2) --
depth=250  m=10  payload=  0.07MB allocated=      2MB
depth=500  m=10  payload=  0.14MB allocated=      4MB
depth=1000 m=10  payload=  0.27MB allocated=      7MB
-- Increasing m (depth=1000 fixed): allocated scales roughly linearly with m --
depth=1000 m=5   payload=  0.16MB allocated=      4MB
depth=1000 m=100 payload=  2.57MB allocated=     47MB
That's roughly a 100–130x reduction in peak allocation for the same payload sizes, and no OOM in any of my runs. This looks like it resolves the issue — thanks for the quick fix!

Credit

Reported by Shuji Ohira (@shuji-oh).

References

Related advisories

Is your project exposed to this? Stateward checks every dependency on every pull request and flags it only if your code actually reaches it.

Check my repo

Summarize with AI

ChatGPTClaudePerplexity

Sources: CISA KEV (public domain), OSV.dev & GitHub Advisory Database (CC-BY-4.0), FIRST EPSS, NVD/CWE (public domain). Served live from the Stateward advisory database.