metadata
tags:
- security
- vulnerability
- poc
- protobuf
- google
- uncontrolled-recursion
- cwe-674
- dos
license: mit
protobuf — json_format.ParseDict() Uncontrolled Recursion (PoC)
Repo: MBM7/protobuf-parsedicts-recursion-poc
Status: Responsible disclosure — submitted to Huntr
Severity: High / CWE-674
Package: protobuf (PyPI) — google.protobuf
Summary
A 7.4 KB crafted JSON payload with 200 nesting levels crashes any Python
server calling ParseDict() with a RecursionError — unhandled, terminates
the process.
| Payload size | Depth | Result |
|---|---|---|
| 7,443 bytes | 200 | RecursionError ✓ |
| 9,293 bytes | 250 | RecursionError ✓ |
Root Cause
google/protobuf/json_format.py, _ConvertMessage():
def _ConvertMessage(value, message, path):
for field_name, field_value in value.items():
...
_ConvertMessage(field_value, sub_message, path) # ← no depth check
ParseDict() → _ConvertMessage() recurses once per nesting level
with no depth limit. Compare to the binary parser which has
DEFAULT_RECURSION_LIMIT = 100.
Asymmetry
| Parser | Depth limit |
|---|---|
ParseFromString() (binary) |
DEFAULT_RECURSION_LIMIT = 100 ✅ |
ParseDict() (JSON→proto) |
NONE ❌ |
MessageToJson() |
NONE ❌ |
MessageToDict() |
NONE ❌ |
Attack scenario
# Server receives JSON from user, converts to proto:
user_json = json.loads(request.body)
proto_msg = MyMessage()
ParseDict(user_json, proto_msg) # ← CRASH at depth 200
A 7.4 KB JSON payload with 200 nesting levels crashes the Python process.
Reproduce
pip install protobuf
python poc_protobuf_recursive_json.py
Expected:
Nesting depth : 200 levels
JSON payload : 7,443 bytes
Result : RecursionError — server CRASH ✓
Suggested Fix
def _ConvertMessage(value, message, path, depth=0):
if depth > _MAX_RECURSION_DEPTH: # e.g. 100, matching binary parser
raise ParseError('Message too deeply nested')
for field_name, field_value in value.items():
...
_ConvertMessage(field_value, sub_message, path, depth + 1)
Environment
| Package | Version |
|---|---|
| protobuf | 5.29.6 |
| Python | 3.12 |