Hello there, ('ω')ノ
1. 概要:ガジェットとは何か(復習)
- プロトタイプ汚染そのものは「Object.prototype にプロパティを追加できる状態」。
- でも実害にするには ガジェット(gadget) が必要:アプリがそのプロパティを検証無しで読み取り、危険な使い方(例えば
innerHTMLに放り込む、script.srcとして使用する、evalに渡す等)をする箇所です。 - ガジェットを見つけること=脆弱性を“実害”に変える鍵、です。
2. 手動でガジェットを見つけるステップ(詳細・実践向け)
準備(Burp のブラウザを使う想定)
- Burp を使えること(Proxy / Intercept / 内蔵ブラウザ)を前提に説明します。ブラウザのデバッグ機能だけでも同様の手順は可能です。
- ターゲットのページが読み込む JavaScript ファイルを特定する(どのスクリプトに注目するかを決める)。
手順(1)対象スクリプトのレスポンスをインターセプトする
- Burp → Proxy → Options → Intercept server responses を有効にして、レスポンスを止められるようにする。
- 標的ページを読み込み、目的の JS ファイルのレスポンスが表示されたらインターセプトを止める(レスポンスを捕まえる)。
手順(2)スクリプト先頭に debugger を仕込む
捕まえたレスポンスの JS に
debugger;を先頭に挿入してレスポンスをそのまま続行してブラウザに返す。- 目的:ブラウザがそのスクリプトを実行する際に「デバッガで止まる」ようにするため。
手順(3)ブラウザでスクリプトが停止している間にプロトタイプに「読み取り検出」を仕掛ける
- Burp のブラウザ(または開発者ツールのコンソール)で、次のコードを実行して特定のプロパティがいつ読まれるかを検出します。
'YOUR-PROPERTY'を検査したいプロパティ名に置き換えてください。
Object.defineProperty(Object.prototype, 'YOUR-PROPERTY', { get() { console.trace(); return 'polluted'; } })
- この仕組み:
Object.prototype.YOUR-PROPERTYがどこかで読まれると、その getter が呼ばれてスタックトレースがコンソールに出ます。読み出し箇所のスタックから、どの行のコードが参照しているかを特定できます。
手順(4)スクリプトの実行再開 → 呼び出し追跡
- スクリプトのデバッグを継続(再開)すると、getter が呼ばれた時点でコンソールにスタックトレースが出るはずです。
- スタックトレース内の該当行をクリックすると、該当のソース位置へジャンプできます(minified な場合は難しいこともある)。
- ブレークポイントやステップ実行で、そのプロパティがどのように最終的に sink(innerHTML, script.src, eval 等)へ渡されるかを確認します。
手順(5)複数プロパティで繰り返す
- アプリやライブラリが参照しそうなプロパティを順に仕掛けていきます(例:
transport_url,isAdmin,authenticated,apiEndpointなど)。 - スタックトレースが出たらその読み出しを詳しく追い、実際に危険な sink に到達するかを確かめます。
手順(6)ポテンシャル exploit の組み立て
- もし
transport_urlのようなプロパティがscript.srcに使われているのを確認したら、プロトタイプ汚染ソース(例:query string, JSON)を使って実際にObject.prototype.transport_urlを与え、ページで任意のスクリプトを読み込めるか試すことで PoC 作成ができます。
3. DOM Invader を使った自動化(概念と利点)
- 手動は確実だが時間がかかる(特に大規模なサイトや多くのサードパーティライブラリがある場合)。
- DOM Invader(Burp の組込ブラウザ内の機能)は、「自動でプロパティ注入を試し」「プロパティが読み出される箇所を検出」し、場合によっては DOM XSS の PoC を自動生成します。
利点:
- 数分で多数の候補を検出できる(手作業だと数時間〜数日かかる場合あり)。
- ミニファイされたスクリプトや複雑なライブラリ依存を逐一読む必要が減る。
- ただし自動検出は誤検知や過小検出が出るため、出力結果は必ず手動で精査してください。
補足:DOM Invader の具体的なUI操作や設定はツールのドキュメント・チュートリアルを参照してください。 (ここでは原理と流れを理解することを目的にしています)
4. 実務で使えるコツ・チェックリスト(ガジェット探索の早道)
探すべき「怪しい」パターン
- 外部リソース組み立て:
script.src,img.src,iframe.src,link.hrefなどを動的に組み立てている箇所。 - DOM 直接挿入:
innerHTML,insertAdjacentHTML,outerHTML(サニタイズ無し) - 動的コード実行:
eval(),new Function(),setTimeout(string)等に文字列を渡す処理 - ライブラリの
options/configを未定義時に参照しているコード(プロパティが無ければ prototype を参照する実装が危険) - サードパーティウィジェット(広告、チャット、解析)に渡す設定値
高速探索のテクニック
- ユニークな検証キー を使う:
pp_test_YYYYMMDD_RAND(他のロジックを汚さない) - getter トリック:
Object.defineProperty(Object.prototype, 'X', { get() { console.trace(); ... }})で読み出し位置を特定 - hasOwnProperty の確認:目星をつけたオブジェクトが自身のプロパティなのかプロトタイプ経由なのかを判別
- constructor.prototype のトリックも試す:
constructor[prototype][X]経由で汚染できる実装がある(__proto__を防いでいる実装でも有効なことがある) - ログ保存:どの URL / パラメータで何を試したかを記録しておく(再現性のため)
確認後のクリーンアップ
- ブラウザの Object.prototype に値を入れた場合は必ず
delete Object.prototype.Xで消す。 - 可能ならブラウザを再起動して副作用を完全に除去。
5. よくある落とし穴(初心者がハマるポイント)
- minified な JS はそのまま読んでも分かりにくい:自動ツールやソースマップ(存在すれば)を活用する。
hasOwnPropertyがあるとガジェット無効:オブジェクト自身に同名プロパティがある場合、prototype の値は使われない。- プロトタイプを
nullにしている実装:Object.create(null)を使っている場合は継承がないためプロトタイプ汚染の影響を受けない。 - 誤検知に注意:getter を置いてアクセスが確認できても、それが実際に危険な sink に到達しないケースは多い(高確率で false positive)。必ずステップ実行で sink まで追う。
まとめ(短く)
- 手動でのガジェット発見は確実だが時間がかかる。
Object.prototypeに getter を定義してスタックトレースで読み取り位置を特定する手法がとても強力。 - DOM Invader は自動化の強い味方で、多くのケースを素早く見つけられるが、結果の精査は必須。
- 発見したら PoC を作って「実際にどう悪用できるか」を示すことが重要(ただし必ず許可済み環境で行う)。
Best regards, (^^ゞ