CVE-2026-0994 — Google Protobuf JSON Any Parsing DoS (Python)
CVE Name: Recursive JSON Any Parsing Denial-of-Service
CVE ID: CVE-2026-0994
CVSS Score: 8.2 (High)
Severity: High – Denial of Service resulting in application crash or worker outage
Exploitability: Attackers can send crafted JSON to trigger excessive recursion without any privileges
Exploit Availability: Public proof-of-concept exists in public issue threads
Official Patch / Upgrade: Link provided below
What This Vulnerability Actually Is
This vulnerability affects how the Python implementation of Google Protocol Buffers (protobuf) handles JSON input that includes deeply nested Any type messages.
In many Python services, developers receive JSON payloads from clients, convert that JSON into protobuf message objects, and then process those objects. The conversion function most people use is json_format.ParseDict() from the google.protobuf library.
Within protobuf, there’s a special message type called Any. Its job is to wrap arbitrary message types in a standardized envelope. You can think of Any as a generic container. The problem with CVE-2026-0994 is that when ParseDict() encounters nested Any containers, the code that handles them did not count those nested structures toward the recursion limit that the library is supposed to enforce.
In simpler terms:
- The parser tries to limit how deep the JSON can nest to prevent runaway recursion
Anycontainers were being handled in a code path that didn’t count toward that limit- Attackers can craft JSON with many levels of
Anywrapping insideAny - The parser keeps going until Python’s recursion limit is hit
- Once Python hits that limit, it raises a
RecursionErrorand the process may crash
Most services don’t catch this specific exception in a controlled way, so the result is often a crashed worker or an entire service outage on endpoints that parse JSON into protobuf.
How an Attacker Could Exploit This
If you operate a service or function that takes JSON from external clients and converts it to protobuf messages using standard tooling like json_format.ParseDict(), here’s what an attacker can do:
- Locate an endpoint that accepts JSON — REST API, gRPC JSON transcoding, webhook receivers, or any client-driven input endpoint.
- Create a JSON document where each object wraps another object via
Any. That looks like many nested layers of{"@type": "...google.protobuf.Any...", "value": { ... }}. - Send this payload to your API or ingestion service.
- The protobuf parser starts decoding and goes deeper with each nested
Any. - Because the parser fails to count those against its recursion checks, the call stack grows until Python itself refuses to go deeper and throws a
RecursionError. - If your service does not catch and handle this exception safely, that worker dies, your thread exits, or the service may restart.
From an attacker’s perspective, this is a very low-effort attack. You don’t need special credentials. You don’t need to be authenticated. You only need to find an endpoint that reaches protobuf JSON parsing logic.
What Happens Inside the Code
At its core, this flaw happens because of a subtle mistake: the part of the code responsible for unpacking Any messages bypassed the function that tracks recursion depth. In typical protobuf parsing, a counter increments at each nested level so that developers can supply a max_recursion_depth value. That counter ensures untrusted input can’t bog the parser down forever.
Unfortunately, the portion that handles Any wraps turns that logic off for every nested Any. That means you can nest dozens or hundreds of layers without tripping the safety check. Python will keep pushing new stack frames for each level until it simply says, “no more” — and raises a RecursionError.
Proof-of-Concept (PoC) Behavior
The publicly discussed PoC doesn’t exploit any remote service; it runs locally against the vulnerable library to demonstrate the failure mode.
A typical PoC will:
- Construct a JSON object with repeated nested
Anyobjects - Call
ParseDict()with a strictmax_recursion_depthsetting like5or10 - Observe that the library does not throw a normal parse-error at the configured depth
- Instead, Python throws
RecursionError: maximum recursion depth exceededbecause the recursion limit got bypassed
This tells us two things:
- The depth check isn’t functioning correctly for nested
Any - Python runs out of stack before the library throws a controlled exception
In real attack scenarios, you’d see this behavior manifest as timeouts, crashes, or worker exits.
Signs of a Successful Exploit or Attempt
Detecting exploit attempts or actual crashes involves watching logs and service behavior very closely.
App Logs
Watch your application logs for:
- Python
RecursionErrortracebacks - Stack frames pointing into protobuf JSON parsing modules
- Abnormal error rates or crashes immediately after receiving JSON input
For example, if your service logs contain entries like:
RecursionError: maximum recursion depth exceeded
at google.protobuf.json_format
at _ConvertAnyMessage
that’s a clear sign someone fed deeply nested Any containers to your endpoint.
Service Behavior
- Worker processes restarting frequently
- Sudden rise in 5xx responses from JSON intake endpoints
- Timeouts on endpoints immediately after receiving large JSON bodies
How to Detect Exploit Attempts
You can use several detection techniques depending on your logging and infrastructure stack.
Log Monitoring
Install alerts that look for:
- RecursionErrors containing protobuf parser references
- Exception messages where parsing fails with stack frames pointing to JSON parsing logic
Request Inspection
Count how many times "@type":"type.googleapis.com/google.protobuf.Any" shows up in incoming JSON. A normal JSON request usually has one or two such entries. Attack attempts carry dozens or hundreds.
For example:
- Alert if occurrence count of any
@typemention > 10 within a single request body - Reject requests where JSON nesting depth exceeds conservative thresholds
WAF / API Gateway Rules
If you have a Web Application Firewall or API gateway:
- Set rules to reject JSON with excessive nesting
- Set safeguards for deeply nested containers of similar types
- Optionally, drop any untrusted JSON that contains repeated
Anycontainers
Detection Signatures You Can Use
Whether you use SIEM, ELK, Splunk, or another system, here are simple detection examples:
Error Log Pattern
Capture any Python exception with:
RecursionErrorgoogle.protobuf.json_format_ConvertAnyMessage
This combination strongly signals an exploit attempt using deep nesting.
JSON Content Monitoring
Inspect request bodies for repeated patterns:
"@type":"type.googleapis.com/google.protobuf.Any"
If a single payload contains that more than N times (you define N), flag it.
How to Fix It
This vulnerability is not something you can reliably guard against by just filtering inputs — the underlying library itself must be fixed.
Google’s protobuf project has already addressed this by fixing the code path so that nested Any parsing correctly increments and checks the recursion depth counter.
You should:
- Upgrade the Python protobuf package to a version that includes the patch
→ Official patch/upgrade: https://github.com/protocolbuffers/protobuf/pull/25239 - Rebuild and redeploy services that depend on
google.protobufafter the upgrade - Test JSON intake paths after the upgrade to confirm they no longer crash when faced with deeply nested
Anytypes
Short-Term Mitigations
If you can’t immediately upgrade:
- Add strict input validation at the edge (API gateway, firewall)
- Reject payloads with excessive nesting or repeated
Anymarkers - Limit execution time or CPU usage for JSON parsing functions
- Catch and handle
RecursionErrorcleanly so workers don’t crash
These don’t fix the vulnerability, but they reduce the risk of service outage.
Final Takeaway
CVE-2026-0994 lets attackers crash or disrupt Python services that use protobuf JSON parsing because Any containers didn’t count against the recursion limit. Attackers exploit this by sending many nested Any messages. You’ll see Python recursion errors in logs and service instability. To fix this correctly, you must upgrade to the protobuf release that includes the official patch linked above. In the meantime, use strong input filtering and log-based detection to catch and block malicious payloads.
