BONUS-12-2007:mod_security POST Rules Bypass Vulnerability

(Last Updated On: 2018年8月16日)

Stefanさんの承諾を得て日本語訳を公開しています。このブログの「the Month of PHP Bugs」カテゴリでMOPBの翻訳ページを一覧できます。分かりやすいように意訳できる部分は意訳します。厳密に原文の通り訳していないので正確性を重視される方は原文をご覧ください。

■クレジット
発見者:Stefan Esser
攻撃コード:Stefan Esser

■PoCまたは攻撃コード
不必要

■リファレンス
なし

■サマリ
mod_securityはよくPHPと一緒に用いられるWeb Application Firewallです。mod_securityは一般に正規表現で定義されたパターンによって攻撃を防止します。このため、mod_securityは送信されてきたWebアプリケーションのパラメータとなる値と思われるHTTPリクエストをパースし、定義されたパターンに一致するリクエストをブロックします。

mod_securityがapplication/x-www-form-urlencoded コンテントタイプのPOSTリクエストをパースするする際、ASCIIZバイト(訳注:筆者は通常、ヌルバイトまたはヌル文字と呼んでいます)でエンコードされていない以降のポストデータの終わりと間違え、それ以降をスキャンしません。これによりルールを簡単にバイパスできます。

■影響するバージョン
mod_security 2.1.0以下

■詳細情報
mod_securityがリクエストを受け付けWebアプリケーションのパラメータとしてパースする場合、mod_securityが正しいと思う方法でパースします。mod_securityはRFCによって定義されるルールに従ってデータが送信される物としてパースします。実際にPerl、Python、Java、PHPがHTTPリクエストをパースする方法でパースしません。この為、RFCと解釈するアプリケーションとミスマッチが発生し、いくつものルールバイパスの脆弱性が存在します。

そのうちの一つの違いがapplication/x-www-form-urlencodeコンテントタイプのPOSTデータにASCIIZバイトが現れた場合の処理です。mod_securityはPOSTデータをC言語の文字列として扱っているため、最初のASCIIZバイト以降は処理せず、データの終わりとして扱ってしまいます。

mod_securityにとっては残念な事に、他のスクリプト言語パーサのASCIIZバイトの取り扱いは異なります。ほとんどのスクリプト言語(Perl、Python、…)はASCIIZバイトを無視し、データをパースします。PHP 5.2.0以降のPHPもこの動作が適用されます。

■PoC、攻撃コードまたは再現手順
問題を再現するには標準インストールのmod_security 2.1.0とPHP 5.2.0をインストールします。(訳注:ソースからインストールする)

以下のXSSに脆弱なPHPスクリプトをWebのドキュメントルートに配置します。(もちろんインターネットに接続していないサーバで)

<?php
  if (isset($_POST['var'])
    echo($_POST['var']);
?>

以下のコマンドを実行します。

$ echo -e "&var=<script>alert(/xss/);</script>" > postdata
$ curl http://localhost/test.php --data-binary @postdata -A HarmlessUserAgent
<script>alert(/xss/);</script>

この例はブロックされません。(これがデフォルトです)しかし、error.logにXSSの可能性が検知されたとこが記録されています。

今度は同じ事をASCIIZバイトを埋め込んで行います。

$ echo -e "\000&var=<script>alert(/xss/);</script>" > postdata
$ curl http://localhost/test.php --data-binary @postdata -A HarmlessUserAgent
<script>alert(/xss/);</script>

mod_securityはASCIIZバイト以下のパラメータを処理できないので、今回はerror.logにログメッセージは記録されていません。

■備考
全てのスクリプト言語(と稀にパースをアプリケーション自身で行っているもの)はHTTPデータを異なった方法でパースしています。全ての方言を正しく解釈できる単一のパーサ(mod_security)を記述するのは可能ではありません。従って、使用されている言語によって、mod_securityをバイパスするいくつもの方法が存在します。この例はもっとも簡単な例です。

投稿者: yohgaki