カテゴリー
Computer Programming

PHP開発者とユーザが知っておくべきシェルコマンドエスケープの内部処理

(Last Updated On: 2018/08/13)

これから紹介する脆弱性は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