/attacking-saml
Attack SAML single sign-on by decoding and tampering with signed XML assertions — XML signature wrapping (XSW1-XSW8), signature stripping, assertion and attribute tampering, NameID comment injection, XXE through the SAML parser, certificate faking, recipient confusion and
$ npx -y skills add trilwu/secskills --skill attacking-saml --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/attacking-saml
Context preview
The summary Claude sees to decide when to auto-load this skill.
Attack SAML single sign-on by decoding and tampering with signed XML assertions — XML signature wrapping (XSW1-XSW8), signature stripping, assertion and attribute tampering, NameID comment injection, XXE through the SAML parser, certificate faking, recipient confusion and
SKILL.md
attacking-saml.SKILL.mdname: attacking-saml
description: Attack SAML single sign-on by decoding and tampering with signed XML assertions — XML signature wrapping (XSW1-XSW8), signature stripping, assertion and attribute tampering, NameID comment injection, XXE through the SAML parser, certificate faking, recipient confusion and IdP-initiated replay, and Golden SAML forgery. Use when a request carries a `SAMLRequest` or `SAMLResponse` parameter, when base64+deflate decodes to XML with `<saml:Assertion>`, when the target exposes a `/saml/acs` or `/sso` endpoint, when an IdP-initiated login lands an assertion at the SP, or when federation runs through Okta, ADFS, Ping, or Azure/Entra.
verified: 2026-07-26
Attacking SAML
SAML security is XML signature validation, and XML signatures are notoriously hard to validate correctly. The assertion is signed — but which element is signed, what the XML parser reads, and what the application ultimately trusts can be three entirely different things. That gap is XML Signature Wrapping, and it is the reason SAML breaks far more often than its cryptography suggests.
Only against systems you are authorized to test.
When to Use
- A request or redirect carries a `SAMLRequest` or `SAMLResponse` parameter
- Base64 (plus DEFLATE for the redirect binding) decodes to XML containing
`<saml:Assertion>`, `<samlp:Response>`, or `<saml:Subject>`
- The target exposes a `/saml/acs`, `/sso`, `/saml2/acs`, or `/Shibboleth.sso`
Assertion Consumer Service endpoint
- An IdP-initiated login POSTs an assertion straight to the SP with no prior
`AuthnRequest`
- Federation runs through Okta, ADFS, PingFederate, Azure/Entra, Shibboleth,
SimpleSAMLphp, or Keycloak
- You control a low-privilege federated account and want to reach another
identity or role
When NOT to Use
- **The SSO flow is OAuth 2.0 or OpenID Connect** (`code`, `id_token`,
`access_token`, `/authorize`, `/.well-known/openid-configuration`) — use `attacking-oauth-oidc`
- **The token is a JWT** (`eyJ...` with dot separators) rather than signed
XML — use `attacking-jwt`
- **General web testing** with no SAML artifact in scope — use
`testing-web-applications`
- **Golden SAML against a compromised AD FS server or cloud tenant** where you
already hold the token-signing key — that is a tenant-takeover play in `attacking-entra-id`
- **Evaluating the signature primitives themselves** (SHA-1 vs SHA-256, RSA
key size, canonicalization algorithm choice) — use `reviewing-cryptography`
Decode and Inspect
Everything starts with reading the message. The wire format depends on the binding.
# 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 -
The redirect binding uses raw DEFLATE — window bits `-15`, no header. The POST binding does not deflate at all. Guess wrong and you get garbage; try both.
**SAML Raider** (Burp extension) is the working environment: it intercepts the ACS POST, pretty-prints the XML, imports the IdP certificate, and drives the signature-wrapping attacks below with a single click. Install it before doing anything by hand.
**samltool.com** (SAMLtool by OneLogin) decodes and re-encodes messages and is useful for understanding structure — but treat it as offline reference only. Never paste a live production assertion into a third-party website; decode with the commands above instead.
Read these fields first, because they define every attack that follows:
- `<ds:Signature>` — where it sits and what its `<ds:Reference URI="#...">`
points to. This is the whole game.
- `<saml:Subject><saml:NameID>` — the identity being asserted
- `<saml:AttributeStatement>` — roles, groups, email, entitlements
- `<saml:Conditions>` — `NotBefore`, `NotOnOrAfter`, `AudienceRestriction`
- `<saml:SubjectConfirmationData>` — `Recipient`, `NotOnOrAfter`, `InResponseTo`
- `Destination` and `Issuer` on the response envelope
XML Signature Wrapping (XSW)
The core SAML attack. The signature is cryptographically valid, but you move the signed element somewhere the signature checker still accepts it while the application logic reads a *different*, unsigned assertion that you control. The verifier and the consumer disagree about which element is authoritative.
The eight canonical variants (Somorovsky et al.) differ in where the original signed assertion is relocated and where the forged one is injected:
- **XSW1 / XSW2** — wrap the whole `<Response>`; the signature references the
original response while a forged response is processed. XSW2 uses a non-enveloping signature position.
- **XSW3 / XSW4** — assertion-level. XSW3 makes the forged assertion a
*sibling* of the signed one; XSW4 *nests* the signed assertion inside the forged one. Same or overlapping IDs.
- **XSW5 / XSW6** — the copied assertion envelopes the signature; play games
with the `ID` attribute so the reference still resolves.
- **XSW7 / XSW8** — hide in a loose-schema wrapper. XSW7 puts the *forged*
assertion in an `<Extensions>` element; XSW8 buries the *original* signed one in an `<Object>` so it validates but is never consumed.
# 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
Iterate through **all eight**. Which one works depends entirely on the SP's XML library and how it selects the assertion to trust — there is no way to predict it, so try every variant before concluding the SP is safe. A successful login as a NameID you never legitimately held is the finding.
Unsigned Assertions and Signature Stripping
Before the wrap
Read more
name: attacking-saml description: Attack SAML single sign-on by decoding and tampering with signed XML assertions — XML signature wrapping (XSW1-XSW8), signature stripping, assertion and attribute tampering, NameID comment injection, XXE through the SAML parser, certificate faking, recipient confusion and IdP-initiated replay, and Golden SAML forgery. Use when a request carries a `SAMLRequest` or `SAMLResponse` parameter, when base64+deflate decodes to XML with `<saml:Assertion>`, when the target exposes a `/saml/acs` or `/sso` endpoint, when an IdP-initiated login lands an assertion at the SP, or when federation runs through Okta, ADFS, Ping, or Azure/Entra. verified: 2026-07-26
Attacking SAML
SAML security is XML signature validation, and XML signatures are notoriously hard to validate correctly. The assertion is signed — but which element is signed, what the XML parser reads, and what the application ultimately trusts can be three entirely different things. That gap is XML Signature Wrapping, and it is the reason SAML breaks far more often than its cryptography suggests.
Only against systems you are authorized to test.
When to Use
- A request or redirect carries a `SAMLRequest` or `SAMLResponse` parameter
- Base64 (plus DEFLATE for the redirect binding) decodes to XML containing
`<saml:Assertion>`, `<samlp:Response>`, or `<saml:Subject>`
- The target exposes a `/saml/acs`, `/sso`, `/saml2/acs`, or `/Shibboleth.sso`
Assertion Consumer Service endpoint
- An IdP-initiated login POSTs an assertion straight to the SP with no prior
`AuthnRequest`
- Federation runs through Okta, ADFS, PingFederate, Azure/Entra, Shibboleth,
SimpleSAMLphp, or Keycloak
- You control a low-privilege federated account and want to reach another
identity or role
When NOT to Use
- **The SSO flow is OAuth 2.0 or OpenID Connect** (`code`, `id_token`,
`access_token`, `/authorize`, `/.well-known/openid-configuration`) — use `attacking-oauth-oidc`
- **The token is a JWT** (`eyJ...` with dot separators) rather than signed
XML — use `attacking-jwt`
- **General web testing** with no SAML artifact in scope — use
`testing-web-applications`
- **Golden SAML against a compromised AD FS server or cloud tenant** where you
already hold the token-signing key — that is a tenant-takeover play in `attacking-entra-id`
- **Evaluating the signature primitives themselves** (SHA-1 vs SHA-256, RSA
key size, canonicalization algorithm choice) — use `reviewing-cryptography`
Decode and Inspect
Everything starts with reading the message. The wire format depends on the binding.
# 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 -
The redirect binding uses raw DEFLATE — window bits `-15`, no header. The POST binding does not deflate at all. Guess wrong and you get garbage; try both.
**SAML Raider** (Burp extension) is the working environment: it intercepts the ACS POST, pretty-prints the XML, imports the IdP certificate, and drives the signature-wrapping attacks below with a single click. Install it before doing anything by hand.
**samltool.com** (SAMLtool by OneLogin) decodes and re-encodes messages and is useful for understanding structure — but treat it as offline reference only. Never paste a live production assertion into a third-party website; decode with the commands above instead.
Read these fields first, because they define every attack that follows:
- `<ds:Signature>` — where it sits and what its `<ds:Reference URI="#...">`
points to. This is the whole game.
- `<saml:Subject><saml:NameID>` — the identity being asserted
- `<saml:AttributeStatement>` — roles, groups, email, entitlements
- `<saml:Conditions>` — `NotBefore`, `NotOnOrAfter`, `AudienceRestriction`
- `<saml:SubjectConfirmationData>` — `Recipient`, `NotOnOrAfter`, `InResponseTo`
- `Destination` and `Issuer` on the response envelope
XML Signature Wrapping (XSW)
The core SAML attack. The signature is cryptographically valid, but you move the signed element somewhere the signature checker still accepts it while the application logic reads a *different*, unsigned assertion that you control. The verifier and the consumer disagree about which element is authoritative.
The eight canonical variants (Somorovsky et al.) differ in where the original signed assertion is relocated and where the forged one is injected:
- **XSW1 / XSW2** — wrap the whole `<Response>`; the signature references the
original response while a forged response is processed. XSW2 uses a non-enveloping signature position.
- **XSW3 / XSW4** — assertion-level. XSW3 makes the forged assertion a
*sibling* of the signed one; XSW4 *nests* the signed assertion inside the forged one. Same or overlapping IDs.
- **XSW5 / XSW6** — the copied assertion envelopes the signature; play games
with the `ID` attribute so the reference still resolves.
- **XSW7 / XSW8** — hide in a loose-schema wrapper. XSW7 puts the *forged*
assertion in an `<Extensions>` element; XSW8 buries the *original* signed one in an `<Object>` so it validates but is never consumed.
# 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
Iterate through **all eight**. Which one works depends entirely on the SP's XML library and how it selects the assertion to trust — there is no way to predict it, so try every variant before concluding the SP is safe. A successful login as a NameID you never legitimately held is the finding.
Unsigned Assertions and Signature Stripping
Before the wrap
**Security skills for Claude Code that encode the judgment professionals bring, not another wrapper around a scanner.** Offense, defense, and the reverse-engineering and code-audit core both sides share. Every skill is fact-checked against primary sources.

