关于 attacking-saml
面向授权 SAML 安全测试的技能,供检查 SSO 消息和服务提供方行为的测试人员使用。涵盖断言验证缺陷、重放、XML 解析问题及相关身份伪造路径。
主要功能
- 01解码并检查 SAML 消息
- 02测试 XML 签名包装
- 03检查未签名断言处理
- 04评估断言与重放验证
- 05探测 SAML 解析器 XXE
- 06测试证书信任行为
适用场景
- 01评估 SAML ACS 端点
- 02测试签名验证和断言选择逻辑
- 03检查跨服务断言重放
- 04评估 XML 解析与证书信任
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trilwu/ secskills --skill attacking-saml安装后在 Claude Code 中输入/attacking-saml确认已加载。
详细介绍
攻击 SAML
SAML 安全取决于 XML 签名验证,而 XML 签名出了名地难以正确验证。断言经过了签名——但被签名的是哪个元素、XML 解析器读取了什么,以及应用最终信任什么,这三者可能完全不同。这种差异就是 XML 签名包装攻击,也是 SAML 出问题的频率远高于其密码学原理所暗示的原因。
仅可针对获准测试的系统。
适用场景
- 请求或重定向中带有
SAMLRequest或SAMLResponse参数 - Base64 解码后(重定向绑定还需 DEFLATE 解压)得到的 XML 中包含
<saml:Assertion>、<samlp:Response>或<saml:Subject> - 目标暴露了
/saml/acs、/sso、/saml2/acs或/Shibboleth.sso断言消费者服务端点 - IdP 发起的登录会将断言直接 POST 到 SP,且之前没有
AuthnRequest - 联邦认证使用 Okta、ADFS、PingFederate、Azure/Entra、Shibboleth、 SimpleSAMLphp 或 Keycloak
- 你控制着一个低权限联邦账户,并希望获得另一个身份或角色
不适用场景
- SSO 流程使用 OAuth 2.0 或 OpenID Connect(
code、id_token、access_token、/authorize、/.well-known/openid-configuration)——使用attacking-oauth-oidc - 令牌是 JWT(以
eyJ...开头且使用点分隔),而非已签名的 XML——使用attacking-jwt - 常规 Web 测试,范围内没有 SAML 产物——使用
testing-web-applications - 针对已被攻陷的 AD FS 服务或云租户进行 Golden SAML 攻击,且你
已持有令牌签名密钥——这是
attacking-entra-id中的租户接管攻击 - 评估签名原语本身(SHA-1 与 SHA-256、RSA 密钥长度、规范化
算法选择)——使用
reviewing-cryptography
解码与检查
一切都从读取消息开始。线上的格式取决于绑定方式。
# HTTP-Redirect binding: base64 then raw DEFLATE (no zlib header)
echo 'fVLLbtsw...' | base64 -d | python3 -c \
"import sys,zlib; sys.stdout.write(zlib.decompress(sys.stdin.buffer.read(),-15).decode())"
# HTTP-POST binding: plain base64, no deflate
echo 'PHNhbWxwOl...' | base64 -d | xmllint --format -
重定向绑定使用原始 DEFLATE——窗口位数为 -15,不带头部。POST
绑定完全不进行 DEFLATE 压缩。猜错了就会得到乱码;两种方式都试试。
SAML Raider(Burp 扩展)是实际操作环境:它会拦截 ACS POST, 美化 XML 格式、导入 IdP 证书,并通过单击一次即可执行下文介绍的签名包装攻击。开始手动操作前,先安装它。
samltool.com(OneLogin 的 SAMLtool)可以解码并重新编码消息,有助于理解结构——但只能将其作为离线参考。切勿将生产环境中的实时断言粘贴到第三方网站;请改用上述命令进行解码。
先查看以下字段,因为它们决定了后续的所有攻击:
<ds:Signature>——它所在的位置,以及其<ds:Reference URI="#...">指向的内容。这就是关键所在。<saml:Subject><saml:NameID>——被断言的身份<saml:AttributeStatement>——角色、群组、电子邮件、权限<saml:Conditions>——NotBefore、NotOnOrAfter、AudienceRestriction<saml:SubjectConfirmationData>——Recipient、NotOnOrAfter、InResponseTo- 响应封套上的
Destination和Issuer
XML 签名包装(XSW)
SAML 的核心攻击。签名在密码学上有效,但你可以将已签名元素移到签名检查器仍会接受的位置,同时让应用逻辑读取你控制的另一个未签名断言。验证器和使用方对哪个元素具有权威性存在分歧。
八种经典变体(Somorovsky 等人)的区别在于原始已签名断言被移到了哪里,以及伪造断言被插入到了哪里:
- XSW1 / XSW2 — 包装整个
<Response>;签名引用原始响应,而处理的是伪造响应。XSW2 使用非包裹式签名位置。 - XSW3 / XSW4 — 针对断言级别。XSW3 将伪造断言设为已签名断言的同级元素;XSW4 将已签名断言嵌套在伪造断言中。使用相同或重叠的 ID。
- XSW5 / XSW6 — 复制的断言将签名包裹起来;利用
ID属性,使引用仍能解析。 - XSW7 / XSW8 — 藏在宽松架构的包装元素中。XSW7 将伪造断言放入
<Extensions>元素;XSW8 将原始已签名断言藏进<Object>,使其通过验证但永远不会被使用。
# Attack in SAML Raider:
# 1. Intercept the ACS POST, select the signed assertion
# 2. Edit NameID / attributes in the assertion the app will read
# 3. Click XSW1..XSW8 in turn — each rebuilds the document layout
# 4. Forward; watch for a successful login as the forged identity
遍历全部八种。哪种有效完全取决于 SP 的 XML 库以及它如何选择要信任的断言——无法预测,所以在认定 SP 安全之前,先尝试每种变体。若能以一个你从未合法持有过的 NameID 成功登录,即为发现漏洞。
未签名断言与剥离签名
在尝试包装攻击之前,先测试最简单的故障:SP 是否会验证签名?
- 剥离签名。 完全移除
<ds:Signature>并转发。有些 SP 只会在存在签名时才验证。 - 清空签名。 清空
SignatureValue或DigestValue。 - 给错误的对象签名。 只对响应而非断言签名,或反之;当 SP 只检查其中一个时。
- 降级算法。 在 SP 未固定预期算法时,尝试使用 SHA-1 的
SignatureMethod,甚至使用md5/ 无操作算法。
# xmlsec1 can tell you what a document's signature actually covers
xmlsec1 --verify --pubkey-cert-pem idp.crt --id-attr:ID Assertion response.xml
如果 SP 接受被剥离签名或未签名的断言,你就可以完全不使用密码学手段伪造身份——这是严重性最高的 SAML 攻击结果。
篡改断言
一旦你能让断言被接受(剥离签名、未签名,或通过有效的 XSW 变体),就可以更改它所声明的内容:
- NameID — 替换为目标用户(
admin@corp、其他租户的用户) - 属性 — 将
Role/Group/memberOf改为admin,添加 IdP 从未签发的授权属性,将email改为触发 SP 上的账户关联 - AudienceRestriction — 如果 SP 不检查
<Audience>,为同一联合身份体系中的另一个 SP 签发的断言也可在此重放 - NotOnOrAfter / NotBefore — 有效期过长或未受检查时,捕获的断言可在本应过期很久之后重放
- InResponseTo — 对于 SP 发起的流程,检查 SP 是否将响应绑定到自己的
AuthnRequestID;如果没有,注入或重放的响应就会被接受
分别测试每一项。即使 SP 验证了签名,但忽略 AudienceRestriction 或重放时间窗口,仍然完全可被利用。
在 NameID 中注入注释
多个 SAML 库中存在一种经典规范化漏洞(2018 年 Duo / python-saml / OneLogin 类 CVE)。XML 文本节点处理与签名规范化对 XML 注释如何拆分文本节点的处理并不一致。
<saml:NameID>[email protected]</saml:NameID>
签名是基于规范化后的值计算的(注释可能会被丢弃,或文本节点会被拼接),而应用程序的文本提取只读取注释之前的节点——结果是 admin。因此,一个针对 [email protected](你合法拥有的账户)有效签名的断言,会将你认证为 admin。尝试在任何承载身份的字段中插入注释:NameID,以及用于账户映射的 email / username 属性。
通过 SAML 解析器利用 XXE
SP 会将攻击者提供的 XML 输入解析器。如果未禁用外部实体,ACS 端点就会成为 XXE 注入点。
<?xml version="1.0"?>
<!DOCTYPE samlp:Response [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<samlp:Response ...>...&xxe;...</samlp:Response>
使用带外 DTD(http://attacker/evil.dtd)进行盲文件读取,并对 SP 的内部网络发起 SSRF。完整的利用技术——参数实体、带外数据窃取、基于错误的信息泄露——请参阅 exploiting-xxe;SAML 方面的关键在于,断言是注入点,而 ACS 无需身份验证。
伪造证书和使用自签名证书重新签名
如果 SP 未固定 IdP 的证书——而是信任嵌入在 <ds:KeyInfo> 中的任意证书,或接受广泛 CA 证书包中的任何证书——你就可以移除真实签名,使用自己的密钥重新签名,并将自己的证书放入消息中。
# Generate a key/cert and re-sign a tampered assertion
openssl req -x509 -newkey rsa:2048 -keyout evil.key -out evil.crt -days 30 -nodes -subj "/CN=idp"
xmlsec1 --sign --privkey-pem evil.key,evil.crt \
--id-attr:ID Assertion tampered.xml > forged.xml
SAML Raider 可自动完成此操作:“Send Certificate”会导入或生成证书,然后就地重新签名已编辑的消息。如果伪造的自签名断言被接受,说明 SP 信任的是消息中嵌入的密钥,而不是已固定的 IdP 证书——这就完全绕过了身份验证。
Recipient / Destination 混淆和 IdP 发起的重放
- 未检查 Recipient / Destination。 如果 SP 忽略
SubjectConfirmationData中的Recipient或响应中的Destination,那么在一个 SP 捕获的断言就可以在另一个共享该 IdP 的 SP 上重放。 - IdP 发起的重放。 IdP 发起的 SSO 没有可供绑定的
AuthnRequest,因此不存在InResponseTo,而且通常也没有防重放状态。捕获有效的 IdP 发起 POST 并重新发送——在有效期内,它可能直接让你重新登录。日志、引荐来源或历史记录中泄露的断言都能成为可用凭据。 - 跨租户重复使用令牌。 在多租户 SaaS 中,检查为租户 A 签发的断言是否会被租户 B 的 ACS 接受。
Golden SAML
这是最终目标,而非入门手段。如果你已经掌握 IdP 的私有令牌签名密钥(从 AD FS 服务器的证书存储区、Azure/Entra 联合信任或导出的 IdP 密钥库中窃取),就能为任意用户、任意角色伪造无限期有效且签名完美的断言——无需凭据、无需 MFA、无需与 IdP 交互,而且密码重置也无法阻止它。
# With the stolen signing key, mint an assertion for any identity
xmlsec1 --sign --privkey-pem tokensigning.key,tokensigning.crt \
--id-attr:ID Assertion golden.xml > any-user.xml
获取密钥属于主机/租户入侵任务,而非 SAML 消息任务——这条路径(提取 AD FS 令牌签名证书、DKM 密钥或云联合密钥)请参阅 attacking-entra-id。只有在这里塑造伪造断言时才回到本节。
应当摒弃的自我安慰
- “断言已签名,所以无法篡改。” 签名包装可以在保持签名有效的同时,让应用读取不同的元素。已签名不代表被信任的就是已签名的内容。
- “XSW1 不起作用,所以 SP 是安全的。” XSW 有八种变体是有原因的。在得出结论之前,先运行 XSW1–XSW8 全部变体。
- “它会验证签名——我检查过了。” 验证某个签名不等于验证你所使用的断言。而且它可能只在签名存在时才进行验证;移除签名后再测试。
- “受众/接收方只是元数据。” 如果 SP 不强制执行
AudienceRestriction、Recipient和Destination,断言就能在不同 SP 和租户之间重放。 - “注释那件事只是表面问题。”
[email protected]会以admin的身份通过身份验证。规范化不匹配会导致账户完全被接管。 - “这是来自可信 IdP 的 XML,因此不用担心 XXE。” ACS 会先解析字节,再决定是否信任它们;而这些字节来自你的浏览器。它们并不可信。
- “Golden SAML 需要私钥,所以不在范围内。” 伪造断言属于本节范围;请记录其影响,并将密钥提取的工作转到
attacking-entra-id——不要忽视其危害。
ATT&CK 覆盖范围
根据 secskills-core/ttp-index.json 生成——编辑该文件,然后运行
python3 scripts/sync_attack.py --write。在报告中引用 ID 前,请根据
当前 ATT&CK 版本重新核实。
凭据访问(TA0006)
- T1606.002 SAML 令牌——另请参阅
attacking-entra-id
适用于以上任一项的检测内容:engineering-detections。主动搜寻:hunting-threats。入侵后响应:responding-to-incidents。
参考资料
attacking-oauth-oidc—— SSO 流程使用 OAuth 2.0 / OpenID Connect 时attacking-jwt—— 令牌是已签名的 JWT,而非 XML 时exploiting-xxe—— ACS 解析器为攻击目标时的完整 XXE 技术attacking-entra-id—— 通过遭入侵的 AD FS 服务或租户实施 Golden SAML 攻击testing-web-applications—— 相关的 Web 测试方法reporting-security-findings—— 签名绕过和重放的严重性- SAML Raider(Burp 扩展)、Burp Suite、xmlsec1、SAMLtool(samltool.com)