これから紹介する脆弱性はPHP 5.2.6で修正されています。修正された、とは言え注意が必要です。
PHPは古くからシェルコマンドとシェル引数をエスケープ処理する為に、escapeshellcmd関数とescapeshellarg関数を提供しています。
この関数はマルチバイト文字にも対応しているのですが、ビルドや環境によっては対応できていないときがあります。
escapeshellcmd/escapeshellarg関数ではC99で定義されてるmblen関数を利用しています。一般的なUNIX系システムではmblen関数は利用可能でると考えられるので、問題となる事は少ないと思いますが、PHPではphp_mblenマクロが以下のように定義されています。
#ifndef HAVE_MBLEN # define php_mblen(ptr, len) 1 #else # if defined(_REENTRANT) && defined(HAVE_MBRLEN) && defined(HAVE_MBSTATE_T) # define php_mblen(ptr, len) ((ptr) == NULL ? mbsinit(&BG(mblen_state)): (int)mbrlen(ptr, len, &BG(mblen_state))) # else # define php_mblen(ptr, len) mblen(ptr, len) # endif #endif
escapeshellcmd/escapeshellargもphp_mblenを使用しています。mblenが利用できない場合、escapeshellcmd/escapeshellargマルチバイト文字のチェックが行われません。
php_mblenの以下のように利用されているので、チェックされていないことは明白です。(追記:mblenは不正なエンコーディングで負の値を返します)
for (x = 0; x < l; x++) { int mb_len = php_mblen(str + x, (l - x)); /* skip non-valid multibyte characters */ if (mb_len < 0) { continue; } else if (mb_len > 1) { memcpy(cmd + y, str + x, mb_len); y += mb_len; x += mb_len - 1; continue; }
mblenが利用できるシステムでも、mblenやmbsinitの動作はロケールによって決まります。具体的にはLC_CTYPEの設定により変わります。コマンドがどのように実行されるかによって、エスケープ処理が無効となる場合があります。
例えば、WebサーバがCロケールで動作していて、コマンドがSJISで動作している場合、ユーザ入力をコマンドに渡していると危険です。
参考: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-2051