MBM7's picture
Upload 2 files
d6adf2e verified
|
Raw
History Blame Contribute Delete
2.47 kB
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