DOM Clobbering まとめ
知ってはいてもこんなの絶対もう見かけないだろう、と思っていたら最近見かけることになった DOM Clobbering というものについて、備忘録としてメモ書きを残しておきます。クリティカルな攻撃は最近のブラウザでは動かないと思うんですが、実例知ってたら教えていただきたいです。
DOM Clobbering とは
DOM Clobbering とは、端的にいうと、「HTML 要素 の id や name を利用して JS のグローバル変数を作成する攻撃」だと解釈しています。こういうのは実例を見たほうが早いので、簡単な例から見ていこうと思います。
簡単な例
次のようなのをコードを眺めてみましょう。
<html>
<head></head>
<body>
<a href="12345" id="something" foobar="hoge">test</a>
<script type="text/javascript">
console.log(something); // it works for even moden browsers
alert(something.foobar); // it works for IE (< 8?)
</script>
</body>
</html>
実際にコンソールでは以下のように出力され、something という未初期化変数には DOM の a 要素が挿入されているのがわかります。
ただこれだけならバグらせる、あるいはエラーを起こさせる (name="document"
や name="self"
など; 最近のブラウザではこれらは上書きできませんが) くらいで済みます。
ただ IE の場合少し状況が別で、彼らは謎の属性(上の例だと foobar
という謎属性が a 要素に付加されています) を DOM からアクセスできるようにしてくれます。その結果 something.foobar
に値 "hoge"
が格納されるので、例えば IE8 上で ↑ を読ませると、次のようにアラートが表示されます。
これを悪用すれば、任意の変数をそのメンバも含めて構成できることになります。例えば The Spanner の DOM Clobbering で紹介されている例を元に、次のようなコードを考えてみましょう。
<html>
<head></head>
<body>
<form id="self" location="https://shift-js.info">
<script type="text/javascript">
alert("I'll redirect you!");
if (document.location != self.location){
document.location = self.location; // it works for IE8
}
</script>
</body>
</html>
これを最近のブラウザで開いても何も起こらないですよね。ただ IE8 あたりで開くと、グローバル変数 self
が上書きされて(なんと)、リダイレクトされます。
僕が少し試した限りでは、 IE8 以前であればグローバル変数を form
要素を使って上書きできる (a
だと上手く動かなかった)ので、わりかし面倒な脆弱性ですね。
その他は例えば Dec 20: Sanitising HTML – the DOM clobbering issue に掲載されているような攻撃例がシビれます。
仕組み
whatwg にある HTML の Standard の 7.3.3 を読む限り、次の 2 つが自動的に window
のメンバになります(これ解釈違いだったらすみません)。window
のメンバは window.
なしでアクセスできる=グローバル変数になるので、これが上記のような状況を引き出しています。
embed
,form
,frameset
,img
,object
要素の name- 任意種類の要素の id
よって、例えば <a name="hoge"></a>
で hoge
は undefined
ですが、 <form nam="hoge">
や <a id="hoge"></a>
に対して hoge
はそれぞれの要素を指すようになるわけですね。
実際に試してみた、という例は以下にあります。
基本的にこのあたりの仕組みは、古いブラウザに関してはすごくブラウザ依存です。最近は ↑ のように WHATWG Living Standard として記載されていますが、なんとなく動きが違ったりするケースもあるそうです。
実世界での攻撃例
以下 2 つがわりかし記憶にあるやつです。
他、DOM Clobbering について一番マトモに解説されている、Cure53 の Mario さんのスライド にも例が上げられていたので、一度読むことをおすすめします。
対策
開発者側でできる対策としては、次のようなものがあるのではないか、と思います。
- XSS を潰す: 任意の HTML 要素(場合によっては
name
やid
属性を追加できるだけでも良いかもしれない)をドキュメントに挿入できない限りは、DOM Clobbering にはつながりません。 - 未初期化変数を適当に使わない:
var hoge;
なりhoge=null;
なりが指定されていれば、DOM 内のid
やname
による変数の挿入はされないです。 - ユーザー定義の HTML を挿入する場合、id や name に prefix を挿入する: これは Github で取られた対策です。
getElementById
など適切な関数を使う:id="hoge"
な要素にwindow.hoge
でアクセスするのはもうやめよう…。
早い話はしっかり XSS を潰しましょう。ユーザー定義 HTML が入り用の場合には、しっかり属性まで検査しましょう。その際はブラックリスト(onclick
のような JS が指定できるものだけ排除する)での指定は極力避け、ホワイトリストで使える属性値を制限するのがよいと思っています。
あとがき
後方互換性のために残ってる仕様ってたくさんありますよね。
僕は昔のことをよく知らないのですが、そうするとこういう攻撃があるというリスクを考えなくもなるので、やっぱり歴史を学んでおくのも大事かもなあと(それ以外にももちろん理由はありますが)。
若造には大変な世界だなあ。とはいえ、もうそろそろ自分のことを若いと言えない年になってきているんですね…。