{"id":"CVE-2026-113729","kind":"vulnerability","aliases":["CVE-2026-113729","GHSA-5588-rp99-5fjh"],"related":[],"title":"Moov signedxml: Unauthenticated denial of service in exclusive XML canonicalization","description":"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.\n\nFor 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.\n\nThis is the library’s core path for SAML / WS-Federation service providers, which canonicalize attacker-supplied XML prior to authentication.\n\n### Affected\n\n- Package: `github.com/moov-io/signedxml`\n- Function: `ExclusiveCanonicalization` (`exclusivecanonicalization.go`)\n- Verified: **v1.4.0** and current default branch (commit `d79064a`). Earlier v1.2.x–v1.3.x releases are likely affected.\n \n\n### Remediation\n\nDo 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.\n\nFrom @shuji-oh \n```\nThanks for the fix — I re-ran my PoC against this branch and confirmed the memory blowup is gone.\n\nBefore (main)\n\n-- Doubling depth each time (m=10 fixed): allocated grows ~4x each time ≈ O(depth^2) --\ndepth=250  m=10  payload=  0.07MB allocated=     45MB\ndepth=500  m=10  payload=  0.14MB allocated=    179MB\ndepth=1000 m=10  payload=  0.27MB allocated=    711MB\n-- Increasing m (depth=1000 fixed): allocated scales roughly linearly with m --\ndepth=1000 m=5   payload=  0.16MB allocated=    377MB\ndepth=1000 m=100 payload=  2.57MB allocated=   6278MB\nAfter (fix/exc-c14n-namespace-dos)\n\n-- Doubling depth each time (m=10 fixed): allocated grows ~4x each time ≈ O(depth^2) --\ndepth=250  m=10  payload=  0.07MB allocated=      2MB\ndepth=500  m=10  payload=  0.14MB allocated=      4MB\ndepth=1000 m=10  payload=  0.27MB allocated=      7MB\n-- Increasing m (depth=1000 fixed): allocated scales roughly linearly with m --\ndepth=1000 m=5   payload=  0.16MB allocated=      4MB\ndepth=1000 m=100 payload=  2.57MB allocated=     47MB\nThat'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!\n```\n\n### Credit\n\nReported by Shuji Ohira ([@shuji-oh](https://github.com/shuji-oh)).","summary":"The Go library github.com/moov-io/signedxml copies the full namespace map at every node during exclusive XML canonicalization, before any signature check. A small unauthenticated XML document with deeply nested elements and many namespace prefixes can drive multi-gigabyte memory use and crash the process.","severity":"high","cvss":7.5,"epss":0.004650000017136335,"exploited":false,"exploited_at":null,"products":[{"name":"github.com/moov-io/signedxml","fixed":["1.4.1"],"affected":"<= 1.4.0","ecosystem":"go"}],"ecosystems":["go"],"tags":["cwe-400","cwe-407","cwe-770","go","package","saml","ws-federation","xml-signature"],"not_affected_if":["no-xml-signature-parsing","version-1.4.1-or-later"],"how_to_check":"Run go list -m github.com/moov-io/signedxml and check whether the version is 1.4.0 or lower.\nCheck whether the service parses attacker-supplied XML with this library, such as SAML or WS-Federation endpoints.","fix":"Upgrade github.com/moov-io/signedxml to 1.4.1 or later.\nIf upgrading is not possible, unknown.","status":"open","published_at":"2026-10-20T22:19:29+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","triaged_at":"2026-10-23T19:55:37+00:00","updated_at":"2026-10-23T21:57:25+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-23T19:55:37Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade github.com/moov-io/signedxml to 1.4.1 or later.\nIf upgrading is not possible, unknown.","summary":"The Go library github.com/moov-io/signedxml copies the full namespace map at every node during exclusive XML canonicalization, before any signature check. A small unauthenticated XML document with deeply nested elements and many namespace prefixes can drive multi-gigabyte memory use and crash the process.","category":"package","who_cares":["go","saml","ws-federation","xml-signature"],"confidence":0.8,"analyst_note":"","how_to_check":"Run go list -m github.com/moov-io/signedxml and check whether the version is 1.4.0 or lower.\nCheck whether the service parses attacker-supplied XML with this library, such as SAML or WS-Federation endpoints.","needs_analyst":false,"not_affected_if":["version-1.4.1-or-later","no-xml-signature-parsing"],"severity_if_unknown":"high"},"cost_usd":0.000483,"input_tokens":3108,"output_tokens":344,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-113729':13A,21A '-2026':12A,20A '-400':26B '-407':28B '-5588':15A '-770':30B '/2001/10/xml-exc-c14n#':91C '/moov-io/signedxml':24A,45B,95C,208C '/shuji-oh)).':494C '0.07':325C,398C '0.14':334C,407C '0.16':363C,436C '0.27':343C,416C '1':160C '10':310C,323C,332C,341C,383C,396C,405C,414C '100':370C,443C,453C '1000':339C,350C,359C,368C,412C,423C,432C,441C '1024':144C '130x':454C '179mb':337C '2':319C,392C '2.57':372C,445C '250':321C,394C '2mb':401C '377mb':366C '45mb':328C '47mb':448C '4mb':410C,439C '4x':314C,387C '5':361C,434C '500':330C,403C '5fjh':18A '6278mb':375C '711mb':346C '7mb':419C 'accumul':99C 'affect':204C,226C 'alloc':167C,276C,312C,327C,336C,345C,352C,365C,374C,385C,400C,409C,418C,425C,438C,447C,458C 'attack':153C,198C 'attacker-control':152C 'attacker-suppli':197C 'authent':178C,203C 'beevik/etree':140C 'blowup':300C 'branch':217C,295C 'canonic':10A,57B,88C,196C 'cap':141C,263C 'certif':176C 'chain':116C 'check':61B,113C 'commit':218C 'confirm':297C 'control':154C 'copi':46B,96C,230C 'core':186C 'could':162C 'crash':83B 'credit':484C 'current':215C 'cve':11A,19A 'cwe':25B,27B,29B 'd79064a':219C 'declar':122C,269C 'deepli':68B 'default':216C 'defens':262C 'denial':4A 'depth':131C,143C,306C,318C,320C,329C,338C,349C,358C,367C,379C,391C,393C,402C,411C,422C,431C,440C 'digest':110C 'document':66B,155C 'doubl':305C,378C 'drive':76B,163C 'earlier':220C 'element':70B,119C,249C 'entir':98C 'error':273C 'everi':52B,103C 'exclus':8A,55B,86C 'exclusivecanonic':210C 'exclusivecanonicalization.go':211C 'feder':36B,192C 'fix':286C,311C,351C,384C,424C,483C 'fix/exc-c14n-namespace-dos':377C 'full':48B 'function':209C 'ghsa':14A 'gigabyt':79B,166C 'github.com':23A,44B,94C,207C,493C 'github.com/moov-io/signedxml':22A,43B,93C,206C 'github.com/shuji-oh)).':492C 'go':31B,41B 'gone':302C 'grow':313C,386C 'impos':260C 'in-scop':265C 'increas':347C,420C 'introduc':246C 'issu':478C 'level':139C 'librari':42B,184C 'like':225C,474C 'linear':251C,355C,428C 'look':473C 'lookup':259C 'm':130C,136C,146C,309C,322C,331C,340C,348C,357C,360C,369C,382C,395C,404C,413C,421C,430C,433C,442C 'main':304C 'maintain':237C 'mani':72B,123C 'map':50B,101C,234C 'match':175C 'mb':161C,326C,335C,344C,364C,373C,399C,408C,417C,437C,446C 'memori':80B,135C,278C,299C 'moov':1A 'multi':78B,165C 'multi-gigabyt':77B,164C 'namespac':49B,73B,100C,125C,233C,268C 'nest':69B,118C 'node':53B,104C,236C 'o':129C,317C,390C 'oh':282C,491C 'ohira':488C 'oom':169C,466C 'order':158C 'packag':32B,205C 'path':187C 'payload':324C,333C,342C,362C,371C,397C,406C,415C,435C,444C,462C 'peak':134C,457C 'per':138C,235C 'poc':292C 'prefix':74B,126C,137C,245C,253C 'prefix-in-scop':252C 'prior':201C 'process':85B,171C 'produc':128C 'provid':194C 'push/pop':242C 'quick':482C 'ran':290C 'rather':274C 're':289C 're-ran':288C 'reduct':455C 'refer':109C 'releas':223C 'remedi':227C 'replac':250C 'report':485C 'requir':180C 'resolv':476C 'return':271C 'rough':354C,427C,451C 'rp99':17A 'rp99-5fjh':16A 'run':471C 'saml':33B,189C 'scale':353C,426C 'scan':256C 'scope':240C,255C,267C 'servic':6A,193C 'set':258C 'shuji':281C,487C,490C 'shuji-oh':280C,489C 'signatur':39B,60B,112C,174C 'signedxml':2A 'singl':239C 'size':463C 'small':63B,151C 'suppli':199C 'thank':283C,479C 'time':132C,308C,316C,381C,389C 'tree':142C 'unauthent':3A,64B 'unbound':148C,277C 'unus':124C 'use':81B 'v1.2.x':221C 'v1.3.x':222C 'v1.4.0':213C 'valid':173C 'validaterefer':106C 'verifi':212C 'whole':232C 'ws':35B,191C 'ws-feder':34B,190C 'www.w3.org':90C 'www.w3.org/2001/10/xml-exc-c14n#':89C 'xml':9A,38B,56B,65B,87C,200C 'xml-signatur':37B","nicknames":[],"mitigations":[],"who_affected":null,"tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-20T22:19:29+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-5588-rp99-5fjh","url":"https://github.com/advisories/GHSA-5588-rp99-5fjh","severity":"high","published_at":"2026-10-21T16:27:55+00:00","modified_at":"2026-10-21T16:27:58+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","fetched_at":"2026-10-23T19:11:32+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-113729","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-113729","severity":null,"published_at":"2026-10-20T22:19:29+00:00","modified_at":"2026-10-22T16:47:38+00:00","first_seen_at":"2026-10-23T21:57:25+00:00","fetched_at":"2026-10-23T22:29:00+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}