Apache 2.2.25とRewriteLogのエスケープと日本語 (CVE-2013-1862)

先日、Apacheの2.2.25がリリースされた(Apache HTTP Server 2.2.25 Released)。

今回のリリースノートを見ると、CVE-2013-1862(mod_rewriteのログ出力の脆弱性)が修正されたらしい。ということでその辺りを少し書いてみる。

Apacheのログ出力エスケープとap_escape_logitem

まず予備知識として、Apacheアクセスログやエラーログのログ出力時に、制御コードなど特殊な文字はエスケープしてファイルに書き出すようになっている。これは、ログをターミナル(端末)上で見るときに、制御コードの混入によって端末が意図しない動作をするのを防ぐためである(ログファイルを見るのはrootが多いだろうから、攻撃がより有効になる)。

この攻撃手法の名前はあんまり統一されていないけど、Terminal Escape Sequence Injectionとか言うことが多いのかな。サーバへの直接の攻撃ではなく、そのログを見た人のターミナル上で発動する……というのがなかなか面白い。ちなみにこの手の攻撃はWebブラウザ版もあって、昔、不用意にログをブラウザで見せちゃうセキュリティ系のアプライアンスで似た攻撃があった記憶がある(^^;)。

エスケープ処理

実際のログエスケープの処理は、ap_escape_logitem という関数でおこなう。これが中で何をしているのか知りたければ、server/util.cを見てみれば良い。以下のようなソースとなっている(Apache 2.2.25から抜粋)。

AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
{
    char *ret;
    unsigned char *d;
    const unsigned char *s;

    if (!str) {
        return NULL;
    }

    ret = apr_palloc(p, 4 * strlen(str) + 1); /* Be safe */
    d = (unsigned char *)ret;
    s = (const unsigned char *)str;
    for (; *s; ++s) {

        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
            *d++ = '\\';
            switch(*s) {
            case '\b':
                *d++ = 'b';
                break;
            case '\n':
                *d++ = 'n';
                break;
            case '\r':
                *d++ = 'r';
                break;
            case '\t':
                *d++ = 't';
                break;
            case '\v':
                *d++ = 'v';
                break;
            case '\\':
            case '"':
                *d++ = *s;
                break;
            default:
                c2x(*s, 'x', d);
                d += 3;
            }
        }
        else {
            *d++ = *s;
        }
    }
    *d = '\0';

    return ret;
}

まぁ見ればだいたい分かるけど、文字列から制御コードをエスケープして単なる文字列(\nなど)としているだけである。

CVE-2013-1862の修正

今までアクセスログとエラーログはこのエスケープをしていたのだけど、mod_rewriteを利用した際のRewriteLogにはこのエスケープが施されていなかった。そのため、攻撃者はURLに妙な文字列を送り込むことで、そのログを閲覧する管理者のターミナルソフト上に攻撃することが可能となっていたようだ。

なお、昔に修正されたアクセスログとエラーログのエスケープについては、Apache HTTP Server のエラーログ作成時の不備により任意のコードを実行される脆弱性 が日本語で分かりやすいかな。2003年に問題が発見され、Apache 1.3.31およびApache 2.0.49から問題は修正されている。

日本語の文字化け問題

さて、こうなるとひとつ困ったことが起きる。日本語を含むURLはコントロールコードを含むため、今までログでそのまま見えていた文字列がエスケープされてしまうのだ。

たとえば以下のようなhttpd.confを書いていたとする。

RewriteEngine on
RewriteLog "logs/rewrite_log"
RewriteLogLevel 2
RewriteRule ^/(.*)$ /Ok.txt

ここで、[http://target.example.com/日本語を含むURL] というURLにアクセスがあった場合、RewriteLogには以下のように出力される。(見にくいので一部、出力を省略した)

Apache 2.2.24まで
[14/Jul/2013:01:24:08 +0900] (2) init rewrite engine with requested uri /日本語を含むURLを入力
[14/Jul/2013:01:24:08 +0900] (2) rewrite '/日本語を含むURLを入力' -> '/Ok.txt'
[14/Jul/2013:01:24:08 +0900] (2) local path result: /Ok.txt
[14/Jul/2013:01:24:08 +0900] (2) prefixed with document_root to /usr/local/apache-2.2.24/htdocs/Ok.txt
[14/Jul/2013:01:24:08 +0900] (1) go-ahead with /usr/local/apache-2.2.24/htdocs/Ok.txt [OK]
Apache 2.2.25から
[14/Jul/2013:01:22:28 +0900] (2) init rewrite engine with requested uri /\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\xe3\x82\x92\xe5\x90\xab\xe3\x82\x80URL\xe3\x82\x92\xe5\x85\xa5\xe5\x8a\x9b
[14/Jul/2013:01:22:28 +0900] (2) rewrite '/\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\xe3\x82\x92\xe5\x90\xab\xe3\x82\x80URL\xe3\x82\x92\xe5\x85\xa5\xe5\x8a\x9b' -> '/Ok.txt'
[14/Jul/2013:01:22:28 +0900] (2) local path result: /Ok.txt
[14/Jul/2013:01:22:28 +0900] (2) prefixed with document_root to /usr/local/apache-2.2.25/htdocs/Ok.txt
[14/Jul/2013:01:22:28 +0900] (1) go-ahead with /usr/local/apache-2.2.25/htdocs/Ok.txt [OK]

このように、Apache 2.2.25からはログがエスケープされるので日本語がそのまま表示できなくなってしまう。まぁそれが今回の修正だから当たり前と言えば当たり前なんだが。

対策

パッと見でログからURLが追えなくなるので困る場合、以下のようにログをecho -eして、バックスラッシュをエスケープとみなすようにすれば対症療法にはなる。

$ cat rewrite.log | while read -r line; do echo -e $line; done

ただしこの場合、そもそもこの対策がされた根本原因である「混入された制御コードがそのまま表示される」脆弱性は復活するため、注意が必要である。いやはや。