{"id":"EEF-CVE-2026-86696","summary":"Unbounded atom creation from unsigned LogoutResponse status codes in esaml allows atom table exhaustion","details":"## Summary\n\nAllocation of Resources Without Limits or Throttling vulnerability in `esaml` in dropbox esaml allows an unauthenticated attacker to exhaust the Erlang VM atom table and crash the node via unsigned `LogoutResponse` messages.\n\n`esaml:status_code_map/1` in `src/esaml.erl` maps any `StatusCode` value it does not recognize to an atom with `list_to_atom/1` on the last colon-separated segment of the URN. `esaml_sp:validate_logout_response/2` verifies a signature only when the message carries one, so an unsigned `LogoutResponse` posted to the single-logout endpoint reaches `esaml:decode_logout_response/1` and mints one permanent atom per distinct status value. Atoms are never reclaimed and the VM caps the atom table at about one million entries, so an attacker who posts that many distinct values aborts the node.\n\nThis issue affects esaml: from 0.1.0 onward.\n\n## Details\n\n**1. Input.** The single-logout endpoint in `esaml_cowboy:validate_logout/2` reads the request body, decodes the `SAMLResponse` form field with `esaml_binding:decode_response/2`, and passes a `samlp:LogoutResponse` document to `esaml_sp:validate_logout_response/2`.\n\n**2. No signature required.** `validate_logout_response/2` looks for a `ds:Signature` element and verifies it only when one is present. An unsigned document passes through unchanged to `esaml:decode_logout_response/1`, which requires only the `Version` and `IssueInstant` attributes before mapping the `StatusCode` value.\n\n**3. Interning.** `esaml:status_code_map/1` matches six known SAML status URNs to fixed atoms. Any other value starting with `urn:` falls through to a clause that interns the last colon-separated segment with `list_to_atom/1`. The attacker chooses that segment, so each request with a fresh value mints one permanent atom. The VM aborts when the atom table reaches its limit of about one million entries.\n\n## Proof of concept\n\n1. Build an unsigned `samlp:LogoutResponse` document with `Version=\"2.0\"`, any `IssueInstant`, and a `samlp:Status/samlp:StatusCode` whose `Value` is a fresh URN such as `urn:x:\u003cunique token\u003e`.\n2. Deflate and base64-encode it, and post it to the service provider's single-logout endpoint as the `SAMLResponse` form field with `SAMLEncoding` set to the DEFLATE URI.\n3. `esaml_sp:validate_logout_response/2` skips signature verification because none is present, `esaml:decode_logout_response/1` maps the status through `status_code_map/1`, and the atom table grows by one entry per distinct token. The endpoint answers with an error, but the atom is never reclaimed. Repeat with fresh tokens until the VM aborts.\n\n## Impact\n\nAn unauthenticated attacker posts unsigned `LogoutResponse` messages with distinct status code URNs to a service provider's single-logout endpoint. Each message permanently consumes one atom table entry, and about one million of them abort the Erlang VM, denying service to every user of the service provider.\n\n## Configurations\n\nReachable only where the service provider exposes a single-logout endpoint that hands incoming `LogoutResponse` messages to `esaml_sp:validate_logout_response/2`, directly or through `esaml_cowboy:validate_logout/2`. The consume (ACS) path verifies signatures before decoding and does not reach the status code mapping.","aliases":["CVE-2026-86696"],"modified":"2026-10-22T07:00:03.393933843Z","published":"2026-10-22T06:59:07.680Z","database_specific":{"capec_ids":["CAPEC-125"],"cpe_ids":["cpe:2.3:a:handnot2:esaml:*:*:*:*:*:*:*:*","cpe:2.3:a:arekinath:esaml:*:*:*:*:*:*:*:*","cpe:2.3:a:dropbox:esaml:*:*:*:*:*:*:*:*"],"cwe_ids":["CWE-770"]},"references":[{"type":"WEB","url":"https://cna.erlef.org/cves/CVE-2026-86696.html"},{"type":"WEB","url":"https://github.com/handnot2/esaml/commit/be692b56245071899757afa7ef47e43593d3de96"},{"type":"WEB","url":"https://github.com/arekinath/esaml/commit/be692b56245071899757afa7ef47e43593d3de96"},{"type":"WEB","url":"https://github.com/dropbox/esaml/commit/be692b56245071899757afa7ef47e43593d3de96"},{"type":"PACKAGE","url":"https://hex.pm/packages/esaml"}],"affected":[{"package":{"name":"esaml","ecosystem":"Hex","purl":"pkg:hex/esaml"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0.1.0"}]}],"versions":["3.0.1","3.1.0","3.2.0","3.3.0","3.4.0","3.5.0","3.6.0","3.6.1","3.7.0","4.0.0","4.1.0","4.2.0","4.3.0","4.4.0","4.5.0","4.6.0"],"database_specific":{"source":"https://cna.erlef.org/osv/EEF-CVE-2026-86696.json"}},{"ranges":[{"type":"GIT","repo":"https://github.com/handnot2/esaml","events":[{"introduced":"be692b56245071899757afa7ef47e43593d3de96"}]}],"versions":["v4.2.0","v3.6.1","v4.1.0","v4.0.0","v3.6.0","v3.5.0","v3.4.0","v3.3.0","v3.2.0","v3.1.0","v3.0.1","v3.0.0","v2.1.0","2.0.0","v1.1","v1.0"],"database_specific":{"source":"https://cna.erlef.org/osv/EEF-CVE-2026-86696.json"}},{"ranges":[{"type":"GIT","repo":"https://github.com/arekinath/esaml","events":[{"introduced":"be692b56245071899757afa7ef47e43593d3de96"}]}],"versions":["v1.1","v1.0"],"database_specific":{"source":"https://cna.erlef.org/osv/EEF-CVE-2026-86696.json"}},{"ranges":[{"type":"GIT","repo":"https://github.com/dropbox/esaml","events":[{"introduced":"be692b56245071899757afa7ef47e43593d3de96"}]}],"database_specific":{"source":"https://cna.erlef.org/osv/EEF-CVE-2026-86696.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N"}],"credits":[{"name":"Quinn Wilton","type":"FINDER"},{"name":"Quinn Wilton","type":"REPORTER"},{"name":"Jonatan Männchen / EEF","type":"COORDINATOR"}]}