FlawOpen

flawopen.com/Incidents/citrix-bleed-session-token-leak-october-2023

● Citrix · CVE-2023-4966 · October 2023 · CVSS 9.4 · 严重
安全研究 · FlawOpen

Citrix Bleed:会话令牌泄露与 MFA 绕过

CVE-2023-4966 会泄露 NetScaler 内存及会话令牌,攻击者可劫持会话并绕过 MFA。已知利用始于 2023 年 8 月下旬。[1], [2]

💡 通俗易懂的原理解析 (ELI5)

你请助手把一条消息抄到一张小卡片上,并告诉你它有多长。卡片只能写 50 个字符,于是他写了 50 个;可当你问消息有多长时,他如实回答“800 个字符”,因为原文就是那么长。接着你把卡片交给别人,让对方从上面读 800 个字符。对方读完你的 50 个字符后,继续往下读整张纸上其余写着的内容。[2]

核心概念与专有名词

snprintf
把文本格式化写入固定大小缓冲区的 C 函数。文本放不下时会截断,但返回的是完整文本本应有的长度。[3]
NetScaler ADC / Gateway
Citrix 部署在网络边界、提供远程访问和负载均衡的设备。只有配置为 Gateway 或 AAA 虚拟服务器的设备受影响。[1]
缓冲区越界读取(CWE-125)
读取超过缓冲区末尾的数据。不会覆盖任何内容,但相邻内存中的数据会被泄露。 这是对越界读取机制(CWE-125)的具体描述;Citrix 将 CVE-2023-4966 广义归类为 CWE-119。[1], [3]
会话令牌
证明用户已经登录的值。谁出示它,就会被当作该用户,无需密码或 MFA。[2]

框架映射

事件演进时间线

2023-08

据后来的事件响应报告,该漏洞开始作为零日漏洞被利用。[2]

2023-10-10

Citrix 发布安全公告 CTX579459,修复版本为 14.1-8.50、13.1-49.15 和 13.0-92.19。[1]

2023-10-18

CISA 将 CVE-2023-4966 列入已知被利用漏洞目录。[4]

攻击执行流程分解

步骤 1

超长的请求头

一个未经认证、带有超长 Host 请求头的请求,到达设备上生成 OpenID Connect 发现响应的代码。[2], [3]

步骤 2

长度被误读

snprintf 把文本截断以适应缓冲区,却返回完整文本本应有的长度,而代码把它当作实际写入的字节数。[3], [2]

步骤 3

缓冲区之外的内存被发出

响应按这个更大的长度发送,于是设备在截断的 JSON 之外,还返回了相邻的内存。[2]

步骤 4

会话被接管

泄露的内存中可能包含有效的会话令牌。重放其中一个,攻击者就能获得该用户已登录的会话,无需密码或 MFA。[2], [1]

源代码对比:漏洞与安全实现

这是示意性的 C 代码,并非 NetScaler 源代码。修复会检查 snprintf 的返回值,该值表示完整输出本应具有的长度。如果它为负数或不小于缓冲区大小,说明格式化失败或输出被截断,因此函数不会发送任何内容。只有完整响应能放入 buf 时,才会把该长度传给 send_response。[3], [2]

存在漏洞的实现
#include <stdio.h>

int send_response(const char *data, int len);

int send_oidc_config(const char *host)
{
    char buf[256];
    /* n 表示本来会写入的字节数,可能超过 sizeof(buf) */
    int n = snprintf(buf, sizeof(buf), "{\"issuer\":\"https://%s\"}", host);
    return send_response(buf, n);   /* 发送 n 个字节,并读取到 buf 之外 */
}
加固后的安全修复
#include <stdio.h>

int send_response(const char *data, int len);

int send_oidc_config(const char *host)
{
    char buf[256];
    int n = snprintf(buf, sizeof(buf), "{\"issuer\":\"https://%s\"}", host);
    if (n < 0 || (size_t)n >= sizeof(buf))
        return -1;                  /* 拒绝被截断的输出;绝不发送超出缓冲区的数据 */
    return send_response(buf, n);
}

工程与系统安全加固清单

参考来源