YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
apache/avro Python β CWE-674 IPC Handshake Recursion (Unauthenticated Network DoS)
Target Info
| Field | Value |
|---|---|
| Target | apache/avro Python library (IPC layer) |
| Platform | huntr.com |
| Commit | bd70a0859b9d739aad0547aa61bd17291049773b |
| Severity | High (7.5) |
| CWE | CWE-674 Uncontrolled Recursion |
| Attack Vector | Network, unauthenticated, bidirectional |
| Est. payout | $1,500 |
Root Cause
The Avro IPC handshake exchanges protocol declarations as JSON. Both the server (clientProtocol) and client (serverProtocol) parse the peer's protocol with avro.protocol.parse(), which recurses once per nested type level with no depth limit. A type nested ~1005 deep raises RecursionError. On the server this fires inside Responder.respond(), whose only guard is except avro.errors.AvroException β and RecursionError is a RuntimeError, so it escapes and crashes the worker.
ipc.py process_handshake() -> avro.protocol.parse(client_protocol) # attacker bytes
protocol.py parse() -> make_avpr_object() -> _parse_response()
schema.py make_avsc_object() β recurses per nesting level, no cap β RecursionError
ipc.py respond() except avro.errors.AvroException β does NOT catch RecursionError
β worker thread/process dies
Trigger Path
Attacker (client) ββHandshakeRequest{clientProtocol: array nested Γ1005}βββΆ Server
Server: Responder.respond()
ββ process_handshake()
ββ avro.protocol.parse(client_protocol)
ββ make_avsc_object() Γ 1005 β RecursionError
ββ except avro.errors.AvroException β NOT matched (RuntimeError)
β exception escapes respond() β connection worker terminates
Bidirectional: a malicious server crashes Python clients the same way via serverProtocol in read_handshake_response() / read_call_response().
Reproduction
git clone https://github.com/apache/avro.git
cd avro/lang/py && pip install -e .
python3 poc_avro_ipc_cwe674.py
Expected output:
[+] Malicious protocol JSON : 28,250 bytes
[+] Nesting depth : 1005
DEMO 1: End-to-end server crash via Responder.respond()
[+] Framed handshake call_request: 28,287 bytes
[+] CONFIRMED: RecursionError escaped respond()
DEMO 2: Client crash via malicious serverProtocol
[+] CONFIRMED: RecursionError - a malicious server crashes its clients.
DEMO 1 drives the genuine Responder.respond() handler with a wire-format HandshakeRequest β a real server-thread crash, not a simulation.
Suggested Fix
- Depth-limit the parser: thread a
_depthcounter throughmake_avsc_object()and raiseSchemaParseException(anAvroExceptionthe IPC layer handles) past a cap (e.g. 200). - Harden the handler: broaden
respond()'s guard toexcept (avro.errors.AvroException, RecursionError).
The depth limit also closes the file-load recursion path (DataFileReader parsing an embedded schema).
Files
| File | Description |
|---|---|
poc_avro_ipc_cwe674.py |
Working PoC β end-to-end respond() crash + client-side demo |
submission.md |
Full technical writeup (markdown, keep permanently) |
report.md |
Plain prose only β paste into huntr form field |
poc-evidence.html |
Self-contained HTML evidence page for attachment |
README.md |
This file |
Relationship to Other Avro Findings
This is a separate bug, separate folder from the CWE-789 read_array allocation bug (apache-avro-poc/) and the CWE-400 varint O(NΒ²) bug (apache-avro-varint-cwe400-poc/). Distinct root cause (recursion in the protocol/schema parser), distinct attack surface (network IPC handshake vs. file load), and the highest-impact delivery path of the set.