web-dev-qa-db-ja.com

XSSサニタイザーをバイパスするために文字エンコードはどのように使用されますか?

PHP htmlspecialchars()関数には、期待される文字セットをオプションのパラメーターとして指定しないと、特定の問題が発生することを別のブログで読みました。

誰かが、文字エンコーディングに関連するいくつかの例を使用して、サニタイズ関数の不適切な使用から生じるXSSエクスプロイトに関するいくつかの基本的なことを説明できますか?

これは最新のブラウザにも影響しますか?

9
XII

問題

文字エンコードの悪用は、フィルターが配置されている場合でもXSSを機能させるための一般的な手法です。動作する状況はいくつかありますが、すべて共通の前提条件があります。

  • 攻撃者はペイロードを文字エンコードAで送信します。
  • フィルタリングまたはサニタジオンを実行するサーバーは、文字エンコードBで動作しています。
  • 被害者のブラウザは、ページを文字エンコーディングAのように解釈しています。

これがどのように発生するかを示す2つの例を見てみましょう。

例1:htmlspecialcharsにエンコーディングパラメータがない

これは、PHPで非常に一般的な光景です。

_echo htmlspecialchars($_GET["query"], ENT_COMPAT | ENT_HTML401);
_

ここでの問題はデフォルトの動作ですPHPは、エンコーディングが指定されていない場合にフォールバックします。From the manual

省略した場合、エンコードのデフォルト値はPHP使用中のバージョンによって異なります。PHP 5.6以降では、default_charset設定オプションがデフォルト値。PHP 5.4および5.5はデフォルトとしてUTF-8を使用します。以前のバージョンのPHPはISO-8859-1を使用します。

つまり、どのエンコーディングPHPが使用するかは、バージョンと構成によって異なります。すばらしいです。つまり、あなたと奈落の底の間に立っているのは、_php.ini_に無害な変更を加える人、またはおそらく何かだけです。サーバーのアップグレードや再インストールと同じくらい簡単です。私も危険な​​生活をするのが好きですが、それほど危険ではありません。

この例はブラウザとは関係がないことに注意してください。ここで問題となっているのはサーバーでありブラウザーではないため、現代でも古いものでも問題ありません。

もちろん、解決策は正しいエンコーディングを指定し、それが応答のHTTP _Content-Type_ヘッダーで指定されていることを確認することです:

_echo htmlspecialchars($_GET["query"], ENT_COMPAT | ENT_HTML401, "UTF-8");
_

例#2:ブラウザーのヒューリスティックが問題

これは、サーバーが応答で使用しているエンコードを指定していない場合(または、ブラウザーがそれを気にする必要があるメタタグでのみ指定している場合)に問題になります。使用するエンコーディングをブラウザに指定しない場合は、推測する必要があります。残念ながら、すべてのブラウザ それはそれほど上手ではありません

ユーザー入力の特定の文字列(たとえば、+ADw-script+AD4-alert(1)+ADw-/script+AD4-)がHTMLページで十分早くエコーバックされる場合、Internet ExplorerはページがUTF-7でエンコードされていると誤って推測する場合があります。突然、そうでなければ無害なユーザー入力がアクティブなHTMLになり、実行されます。

引用符内のペイロードは、UTF-7でエンコードされた<script>alert(1)</script>です。 UTF-8で動作するサニタイザーは、そのペイロードで危険なものを何も認識せずに通過させますが、UTF-7で動作するようにだまされたブラウザーは引き続きそれを実行します。

私が理解しているのは、これが問題である 主に古いバージョンのIE であるということです。しかし、私にはよくわからないので、それが明確にされている別の答えを見ていただければ幸いです。

EDIT:参照してください Xavier59の答え 最新のブラウザで動作する状況。

ソリューション

サーバー上で行う必要があることは、理論的には単純です。以下が常に真であることを確認する必要があります。

  • 応答の文字エンコーディングは、HTTPヘッダーで正しく設定されています。
  • XSSフィルターは、上記で指定されたものと同じエンコードで機能します。

実際には、それを誤解するのは驚くほど簡単です。

12
Anders

これは、Andersの回答への追加として提供されます(これは素晴らしいことです)。

私が理解しているのは、IEのほとんどが古いバージョンであり、これが問題であるということです。しかし、その原因は不明であり、確信が持てないので、別のバージョンを参照してください。それが明確にされているところに答えてください。

はい、これは最新のブラウザに影響します。


次のサニタイズを見てみましょう:

<?php
    header('Content-Type: text/html;charset=utf-8');
    echo preg_replace('/<\w+/', '', $_GET['name']).", can you p0wn it ?"
?>

これは、次の理由により脆弱ではないように見えるかもしれません。

  • <に続く1つ以上の文字が削除されているため、攻撃者は新しいタグを開くことができません。
  • Content-Typeヘッダーがutf-8に正しく設定されている

ここで、%00%3C%00を送信するとします。<%3C)の後に文字(\wで定義されている)ではなく%00(nullバイト)が続くため、正規表現パーサーは失敗します。 UTF-8では、反映された入力は何も実行しませんが、UTF-16に反映させる方法を見つけることができれば...

これが W3から読み取れるもの です。

ファイルの先頭にUTF-8バイトオーダーマーク(BOM)がある場合、Internet Explorer 10または11以外の最近のブラウザーバージョンは、それを使用してページのエンコードがUTF-8であると判断します。 HTTPヘッダーを含む他のどの宣言よりも優先されます

BOMがある場合はメタエンコーディング宣言をスキップできますが、ページのエンコーディングが何であるかを確認するためにソースコードを見る人々を助けるため、それを保持することをお勧めします。

UTF-16BOM文字は、Unicode文字U+FEFFです(さまざまなBOMエンコードについては、 Wikipedia で最もよく説明されています)。したがって、入力はdomの先頭に反映されているため、文字セットをUTF-16に変更して、コードを実行することができます。

完全なペイロード:

%FE%FF%00%3C%00s%00c%00r%00i%00p%00t%00%3E%00a%00l%00e%00r%00t%00(%00%22%00P%000%00w%00n%00e%00d%00%22%00)%00;%00%3C%00/%00s%00c%00r%00i%00p%00t%00%3E

ここに私が作成したPOCがあります。 ほとんどのxss監査人はそれに失敗しませんが、Firefoxはその監査人がデフォルトで無効になっているため失敗します。 (Firefox Nightly 60.0a1でテスト-今日の最終バージョン)

ただし、htmlspecialcharshtmlentitiesは当てはまりません。それにもかかわらず、これは角を曲がったところに常にトリッキーなEdgeケースがあることを示しています!


エンコーディングに対するその他の攻撃には、 文字マッピング が含まれますが、これらは今日でもまだ関連しています。

6
Xavier59

OWASP XSS ページから:

「クロスサイトスクリプティング攻撃は、インジェクションの問題の一種です。悪意のあるスクリプトが無害で信頼できるWebサイトに挿入されます。クロスサイトスクリプティング(XSS)攻撃は、攻撃者がWebアプリケーションを使用して悪意のあるコードを送信するときに発生します。ブラウザ側のスクリプトの形式で、別のエンドユーザーに送信されます。これらの攻撃を成功させる欠陥は非常に広範囲に及び、検証またはエンコードせずに、Webアプリケーションが生成する出力でユーザーからの入力を使用するあらゆる場所で発生します。

攻撃者はXSSを使用して、疑いを持たないユーザーに悪意のあるスクリプトを送信できます。エンドユーザーのブラウザは、スクリプトが信頼されるべきではないことを知る方法がなく、スクリプトを実行します。スクリプトは信頼できるソースからのものであると考えているため、悪意のあるスクリプトは、ブラウザーが保持し、そのサイトで使用されるCookie、セッショントークン、またはその他の機密情報にアクセスできます。これらのスクリプトは、HTMLページのコンテンツを書き換えることもできます。」

これは、ユーザーの入力を無害化しない悪いコーディングプラクティスの例です。

あなたがWeb開発者であり、このファイルをWebサイトに作成するとします(_name.php_):

_<form action="" method="GET">
  What is your name: <input type="text" name="username"><br>
  <input type="submit" value="Submit">
</form>

<?php
  print("Entered name is: ".$_GET["username"]);
?>
_

ブラウザでこのページを開くと、次のようなものが表示されます。

welcome page, asking for username to print it later

名前を付けて、この単純なファイルの動作を見てみましょう。GETメソッドを使用しているため、URLで送信されたデータを確認できます。

welcome page with sample name, not malicious input

しかし、誰かがこのinputボックスにHTMLコードを挿入しようとするとどうなりますか?

_<Marquee><h1>Andrew ng</h1></Marquee>
_

下の画像の結果をご覧ください。

Code Injection in not sanitized field

ユーザーの入力は、ファイルの元のソースコードの一部であるかのようにレンダリングされました。

次に、Javascriptコードで同じことを試してみて、どうなるか見てみましょう。ブラウザーでテストするインジェクションコードは、XSSの2つの方法になります。

_<h1>Andrew</h1><script>alert("XSS");</script>

<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg">
_

どちらの場合もGoogle Chromeはこのスクリプトの実行をブロックしました: 1st XSS Attempt in Chrome

2nd XSS Attempt in Chrome

しかし、Mozilla Firefoxでは両方のスクリプトが正常に実行されます。

1st XSS Attempt in Firefox

2nd XSS Attempt in Firefox

これにより、XSSと最新のブラウザーの現在の状況をよりよく理解できれば幸いです。これは以下でテストされました。

  • Google Chrome 64.0.3282.119(公式ビルド)(64ビット)
  • Mozilla Firefox Quantum 58.0(64ビット)

htmlspecialchars()関数については、詳細情報 here を参照してください。

あなたにとって興味深いXSSの他の例は、私のブログの this one です。

それが役に立てば幸い。

1
galoget