{"id":"CVE-2026-104635","aliases":["EEF-CVE-2026-104635","EUVD-2026-95562","GHSA-m497-c2h9-rvw6","UBUNTU-CVE-2026-104635"],"status":"published","title":"Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages","description":"Uncontrolled Recursion vulnerability in Protobuf.JSON.Decode in elixir-protobuf protobuf allows an unauthenticated remote attacker to crash the decoding process via a deeply nested JSON document. Any application that decodes attacker-supplied JSON with Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, or Protobuf.JSON.from_decoded/3 into a schema that contains a self-referential or cyclic message type is affected.\n\nIn lib/protobuf/json/decode.ex, the embedded-message clause of decode_singular/3 recurses into internal_from_json_data/3 once per nesting level without incrementing or checking the decoder's depth counter. The depth guard increase_depth_and_maybe_throw/1 covers only the Google.Protobuf.ListValue and Google.Protobuf.Struct clauses, so the recursion_limit option has no effect on user-defined message types. Each nesting level allocates a stack frame and heap objects, and a sufficiently deep document exhausts the memory of the decoding process. Confidentiality and integrity are not affected.\n\nThis issue affects protobuf: from 0.8.0 before 0.17.1.","severity":{"version":"4.0","score":8.2,"vector":"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","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-08","score":0.00501,"percentile":0.43163,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-674"],"published":"2026-10-09T08:17:52Z","modified":"2026-10-28T02:30:34Z","fixes":[{"ecosystem":"Hex","package":"protobuf","fixed_in":["0.17.1"],"sources":["osv"]}],"affected":[{"source":"osv","ecosystem":"Hex","package":"protobuf","ranges":[{"type":"SEMVER","events":[{"introduced":"0.8.0"},{"fixed":"0.17.1"}]}],"versions":["0.10.0","0.11.0","0.12.0","0.13.0","0.14.0","0.14.1","0.15.0","0.16.0","0.16.1","0.17.0","0.8.0","0.9.0"]}],"products":[{"vendor":"elixir-protobuf","product":"protobuf"},{"vendor":"python","product":"protobuf"}],"references":[{"url":"https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-m497-c2h9-rvw6","tags":["related","vendor-advisory","advisory","report"]},{"url":"https://cna.erlef.org/cves/CVE-2026-104635.html","tags":["related","web","report"]},{"url":"https://osv.dev/vulnerability/EEF-CVE-2026-104635","tags":["related","exploit","web","report"]},{"url":"https://github.com/elixir-protobuf/protobuf/commit/b0a1d4eaffaf50012fa71a8e931a47cf252d0370","tags":["related","fix","web","report"]},{"url":"https://github.com/elixir-protobuf/protobuf/commit/e9432ad1c4099511905353cebcececa3a1f7c3ff","tags":["patch","fix","report"]},{"url":"https://github.com","tags":["web"]},{"url":"https://repo.hex.pm","tags":["web"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/104xxx/CVE-2026-104635.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-104635","tags":["advisory"]},{"url":"https://github.com/elixir-protobuf/protobuf","tags":["package"]},{"url":"https://hex.pm/packages/protobuf","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-104635","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-104635","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-104635","title":"Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages","description":"Uncontrolled Recursion vulnerability in Protobuf.JSON.Decode in elixir-protobuf protobuf allows an unauthenticated remote attacker to crash the decoding process via a deeply nested JSON document. Any application that decodes attacker-supplied JSON with Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, or Protobuf.JSON.from_decoded/3 into a schema that contains a self-referential or cyclic message type is affected.\n\nIn lib/protobuf/json/decode.ex, the embedded-message clause of decode_singular/3 recurses into internal_from_json_data/3 once per nesting level without incrementing or checking the decoder's depth counter. The depth guard increase_depth_and_maybe_throw/1 covers only the Google.Protobuf.ListValue and Google.Protobuf.Struct clauses, so the recursion_limit option has no effect on user-defined message types. Each nesting level allocates a stack frame and heap objects, and a sufficiently deep document exhausts the memory of the decoding process. Confidentiality and integrity are not affected.\n\nThis issue affects protobuf: from 0.8.0 before 0.17.1.","status":"published","published":"2026-10-09T08:17:52.372Z","modified":"2026-10-15T15:57:11.974Z","cvss":[{"version":"4.0","score":8.2,"vector":"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","level":"high","assessor":"cna"}],"cwes":["CWE-674"],"updated_at":"2026-11-02T14:20:09.448255Z"}],"euvd":[{"id":"EUVD-2026-95562","description":"Uncontrolled Recursion vulnerability in Protobuf.JSON.Decode in elixir-protobuf protobuf allows an unauthenticated remote attacker to crash the decoding process via a deeply nested JSON document. Any application that decodes attacker-supplied JSON with Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, or Protobuf.JSON.from_decoded/3 into a schema that contains a self-referential or cyclic message type is affected.\n\nIn lib/protobuf/json/decode.ex, the embedded-message clause of decode_singular/3 recurses into internal_from_json_data/3 once per nesting level without incrementing or checking the decoder's depth counter. The depth guard increase_depth_and_maybe_throw/1 covers only the Google.Protobuf.ListValue and Google.Protobuf.Struct clauses, so the recursion_limit option has no effect on user-defined message types. Each nesting level allocates a stack frame and heap objects, and a sufficiently deep document exhausts the memory of the decoding process. Confidentiality and integrity are not affected.\n\nThis issue affects protobuf: from 0.8.0 before 0.17.1.","status":"published","published":"2026-10-09T08:17:52Z","modified":"2026-10-15T15:57:11Z","cvss":[{"version":"4.0","score":8.2,"vector":"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","level":"high"}],"updated_at":"2026-11-02T17:00:12.42385Z"}],"nvd":[{"id":"CVE-2026-104635","description":"Uncontrolled Recursion vulnerability in Protobuf.JSON.Decode in elixir-protobuf protobuf allows an unauthenticated remote attacker to crash the decoding process via a deeply nested JSON document. Any application that decodes attacker-supplied JSON with Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, or Protobuf.JSON.from_decoded/3 into a schema that contains a self-referential or cyclic message type is affected.\n\nIn lib/protobuf/json/decode.ex, the embedded-message clause of decode_singular/3 recurses into internal_from_json_data/3 once per nesting level without incrementing or checking the decoder's depth counter. The depth guard increase_depth_and_maybe_throw/1 covers only the Google.Protobuf.ListValue and Google.Protobuf.Struct clauses, so the recursion_limit option has no effect on user-defined message types. Each nesting level allocates a stack frame and heap objects, and a sufficiently deep document exhausts the memory of the decoding process. Confidentiality and integrity are not affected.\n\nThis issue affects protobuf: from 0.8.0 before 0.17.1.","status":"published","published":"2026-10-09T09:17:07.36Z","modified":"2026-10-15T16:17:33.81Z","cvss":[{"version":"4.0","score":8.2,"vector":"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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"}],"cwes":["CWE-674"],"updated_at":"2026-11-02T14:12:55.473789Z"}],"osv":[{"id":"CVE-2026-104635","title":"Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages","description":"Uncontrolled Recursion vulnerability in Protobuf.JSON.Decode in elixir-protobuf protobuf allows an unauthenticated remote attacker to crash the decoding process via a deeply nested JSON document. Any application that decodes attacker-supplied JSON with Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, or Protobuf.JSON.from_decoded/3 into a schema that contains a self-referential or cyclic message type is affected.\n\nIn lib/protobuf/json/decode.ex, the embedded-message clause of decode_singular/3 recurses into internal_from_json_data/3 once per nesting level without incrementing or checking the decoder's depth counter. The depth guard increase_depth_and_maybe_throw/1 covers only the Google.Protobuf.ListValue and Google.Protobuf.Struct clauses, so the recursion_limit option has no effect on user-defined message types. Each nesting level allocates a stack frame and heap objects, and a sufficiently deep document exhausts the memory of the decoding process. Confidentiality and integrity are not affected.\n\nThis issue affects protobuf: from 0.8.0 before 0.17.1.","status":"published","published":"2026-10-09T08:17:52.372Z","modified":"2026-10-28T02:30:34.677095131Z","cvss":[{"version":"4.0","score":8.2,"vector":"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","level":"high"}],"cwes":["CWE-674"],"updated_at":"2026-11-02T15:44:54.236592Z"},{"id":"EEF-CVE-2026-104635","title":"Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages","description":"## Summary\n\nUncontrolled Recursion vulnerability in `Protobuf.JSON.Decode` in elixir-protobuf protobuf allows an unauthenticated remote attacker to crash the decoding process via a deeply nested JSON document. Any application that decodes attacker-supplied JSON with `Protobuf.JSON.decode/3`, `Protobuf.JSON.decode!/3`, or `Protobuf.JSON.from_decoded/3` into a schema that contains a self-referential or cyclic message type is affected.\n\nIn `lib/protobuf/json/decode.ex`, the embedded-message clause of `decode_singular/3` recurses into `internal_from_json_data/3` once per nesting level without incrementing or checking the decoder's depth counter. The depth guard `increase_depth_and_maybe_throw/1` covers only the `Google.Protobuf.ListValue` and `Google.Protobuf.Struct` clauses, so the `recursion_limit` option has no effect on user-defined message types. Each nesting level allocates a stack frame and heap objects, and a sufficiently deep document exhausts the memory of the decoding process. Confidentiality and integrity are not affected.\n\nThis issue affects protobuf: from 0.8.0 before 0.17.1.\n\n## Details\n\n**1. Entry points.** `Protobuf.JSON.decode/3`, `Protobuf.JSON.decode!/3`, and `Protobuf.JSON.from_decoded/3` in `lib/protobuf/json.ex` validate options and call `Protobuf.JSON.Decode.from_json_data/3`, which builds a `state` map carrying `depth: 0` and the `recursion_limit` (default 100). `from_decoded/3` accepts already-parsed data, so the underlying JSON parser never bounds the depth either.\n\n**2. Unguarded recursion.** For a user-defined message module, `internal_from_json_data/3` calls `decode_message/4`, which calls `decode_regular_fields/3` and `decode_oneof_fields/3`. Each field value passes through `decode_value/3` and, for a singular embedded message, reaches the `decode_singular/3` clause matching `embedded?: true`. That clause calls `internal_from_json_data/3` with `state` unchanged, so `state.depth` stays at 0 at every nesting level. Repeated fields and map values reach the same clause through `decode_repeated/3` and `decode_map/3`.\n\n**3. Guard coverage.** `increase_depth_and_maybe_throw/1` increments `depth` and throws `{:recursion_limit_exceeded, limit}` when it exceeds the limit. It is called only from the `Google.Protobuf.ListValue` and `Google.Protobuf.Struct` clauses, which the `Google.Protobuf.Value` clause delegates to. The `recursion_limit` documentation scopes the option to those wrappers, and no other path checks depth.\n\n**4. Result.** A self-referential schema such as a `Tree` message with a `Tree child` field recurses once per JSON nesting level with no bound. Each level holds a live stack frame and allocates heap objects, so a sufficiently deep document exhausts the memory of the decoding process and crashes it. The BEAM `max_heap_size` process flag defaults to unlimited, so repeated or concurrent requests can exhaust the memory of the whole node.\n\n## Proof of concept\n\n1. Define and compile a self-referential proto3 message, for example a `TreeNode` message with a single embedded field `child` of type `TreeNode`.\n2. Build a JSON document that nests the `child` field several thousand levels deep, for example by wrapping `{}` in `{\"child\": ...}` 5,000 times.\n3. Decode it with `Protobuf.JSON.decode(json, TreeNode, recursion_limit: 100)`.\n4. Observe that no `Protobuf.JSON.DecodeError` for an exceeded recursion limit is raised and the decoder walks every level. The same depth in a `Google.Protobuf.Struct` payload raises `{:recursion_limit_exceeded, 100}`. At larger depths the decoding process consumes hundreds of megabytes and crashes with stack and heap exhaustion.\n\n## Impact\n\nAn unauthenticated client that can reach an endpoint decoding JSON into a self-referential message type can crash the decoding process through stack and memory exhaustion with a single request. Repeated or concurrent requests can exhaust the memory of the whole node and disrupt co-located workloads.\n\n## Workarounds\n\nReject JSON documents whose nesting depth exceeds a fixed bound at the web or middleware layer before passing them to the decoder. No decoder option limits recursion for user-defined message types, so the check must happen before `Protobuf.JSON.decode/3`, `Protobuf.JSON.decode!/3`, or `Protobuf.JSON.from_decoded/3` is called.\n\n## Configurations\n\nThe application decodes attacker-controlled JSON with `Protobuf.JSON.decode/3`, `Protobuf.JSON.decode!/3`, or `Protobuf.JSON.from_decoded/3` into a message type whose schema contains a self-referential or cyclic embedded message.","status":"published","published":"2026-10-09T08:17:52.372Z","modified":"2026-10-09T09:30:02.783183553Z","cvss":[{"version":"4.0","score":8.2,"vector":"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","level":"high"}],"cwes":["CWE-674"],"updated_at":"2026-11-02T15:46:33.047032Z"},{"id":"UBUNTU-CVE-2026-104635","description":"Uncontrolled Recursion vulnerability in Protobuf.JSON.Decode in elixir-protobuf protobuf allows an unauthenticated remote attacker to crash the decoding process via a deeply nested JSON document. Any application that decodes attacker-supplied JSON with Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, or Protobuf.JSON.from_decoded/3 into a schema that contains a self-referential or cyclic message type is affected. In lib/protobuf/json/decode.ex, the embedded-message clause of decode_singular/3 recurses into internal_from_json_data/3 once per nesting level without incrementing or checking the decoder's depth counter. The depth guard increase_depth_and_maybe_throw/1 covers only the Google.Protobuf.ListValue and Google.Protobuf.Struct clauses, so the recursion_limit option has no effect on user-defined message types. Each nesting level allocates a stack frame and heap objects, and a sufficiently deep document exhausts the memory of the decoding process. Confidentiality and integrity are not affected. This issue affects protobuf: from 0.8.0 before 0.17.1.","status":"withdrawn","published":"2026-10-13T00:00:00Z","modified":"2026-10-21T23:00:22.106519147Z","cvss":[{"version":"4.0","score":8.2,"vector":"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","level":"high"}],"updated_at":"2026-11-02T16:55:59.130573Z"}]}}
