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 要素が挿入されているのがわかります。

DOM の a 要素が選択されている

ただこれだけならバグらせる、あるいはエラーを起こさせる (name="document"name="self" など; 最近のブラウザではこれらは上書きできませんが) くらいで済みます。

ただ IE の場合少し状況が別で、彼らは謎の属性(上の例だと foobar という謎属性が a 要素に付加されています) を DOM からアクセスできるようにしてくれます。その結果 something.foobar に値 "hoge" が格納されるので、例えば IE8 上で ↑ を読ませると、次のようにアラートが表示されます。

alert で something.foobar を表示

これを悪用すれば、任意の変数をそのメンバも含めて構成できることになります。例えば 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. なしでアクセスできる=グローバル変数になるので、これが上記のような状況を引き出しています。

  1. embed, form, frameset, img, object 要素の name
  2. 任意種類の要素の id

よって、例えば <a name="hoge"></a>hogeundefined ですが、 <form nam="hoge"><a id="hoge"></a> に対して hoge はそれぞれの要素を指すようになるわけですね。

実際に試してみた、という例は以下にあります。

基本的にこのあたりの仕組みは、古いブラウザに関してはすごくブラウザ依存です。最近は ↑ のように WHATWG Living Standard として記載されていますが、なんとなく動きが違ったりするケースもあるそうです。

実世界での攻撃例

以下 2 つがわりかし記憶にあるやつです。

他、DOM Clobbering について一番マトモに解説されている、Cure53 の Mario さんのスライド にも例が上げられていたので、一度読むことをおすすめします。

対策

開発者側でできる対策としては、次のようなものがあるのではないか、と思います。

  • XSS を潰す: 任意の HTML 要素(場合によっては nameid 属性を追加できるだけでも良いかもしれない)をドキュメントに挿入できない限りは、DOM Clobbering にはつながりません。
  • 未初期化変数を適当に使わない: var hoge; なり hoge=null; なりが指定されていれば、DOM 内の idname による変数の挿入はされないです。
  • ユーザー定義の HTML を挿入する場合、id や name に prefix を挿入する: これは Github で取られた対策です。
  • getElementById など適切な関数を使う: id="hoge" な要素に window.hoge でアクセスするのはもうやめよう…。

早い話はしっかり XSS を潰しましょう。ユーザー定義 HTML が入り用の場合には、しっかり属性まで検査しましょう。その際はブラックリスト(onclick のような JS が指定できるものだけ排除する)での指定は極力避け、ホワイトリストで使える属性値を制限するのがよいと思っています。

あとがき

後方互換性のために残ってる仕様ってたくさんありますよね。

僕は昔のことをよく知らないのですが、そうするとこういう攻撃があるというリスクを考えなくもなるので、やっぱり歴史を学んでおくのも大事かもなあと(それ以外にももちろん理由はありますが)。

若造には大変な世界だなあ。とはいえ、もうそろそろ自分のことを若いと言えない年になってきているんですね…。

Written on March 20, 2018