CVE-2012-1823 CVE-2012-2311 PHP の CGI モードにおける脆弱性について

この脆弱性は2012年5月3日にリリースされた PHP バージョン 5.3.12 と 5.4.2 において明らかになりました。CGI モードにて動作させている場合のみ影響を受けますが、対象となる環境ではリモートからソースコード漏洩、任意コード実行を引き起こす攻撃が可能です。

以下のサイトに脆弱性の発見者による解説が公開されています。
Eindbazen PHP-CGI advisory (CVE-2012-1823)

該当するPHPのリリースノートは以下です。
PHP 5.3.12 and PHP 5.4.2 Released!
PHP 5.4.3 and PHP 5.3.13 Released!

既に攻撃も観測されているようです。
PHP-CGI Vulnerability Exploited in the Wild

この脆弱性に対しては CVE-2012-1823 が割り当てられており、2012年5月3日のリリースと同時に修正と情報公開が完了するはずでした。ですが、リリース後に修正が不十分であることが指摘され、残っていた脆弱性に対して、新たに CVE-2012-2311 が割り当てられました。2012年5月8日に不十分であった箇所を修正した PHP バージョン 5.3.13 と 5.4.3 が新たにリリースされて修正が完了しています。

対策等については既に多くのサイトで解説がされていますので、今回の問題についての詳細を解説したいと思います。

以下の表は今回の脆弱性が公開される直前の最新版における検証結果です。ディストリビューションの例として広く使われている CentOS を挙げています。

リリースバージョンPHP バージョン影響
PHP 5.3.115.3.11
PHP 5.4.15.4.1
CentOS 5.8php-5.1.6-32.el5
CentOS 5.8php53-5.3.3-5.el5
CentOS 6.2php-5.3.3-3.el6_2.6

CentOS 5.8 のパッケージ php-5.1.6-32.el5 のみ影響を受けていません。入力パラメータの扱いという意味では、上記のバージョンはすべて同じ問題を抱えていますが、オプションの処理に違いがあるため影響の有無が分かれています。

致命的な攻撃となるのは以下のオプションが使われた場合です。任意コード実行の方が深刻度が高いのでこちらを詳細に解説します。

オプション動作悪用時の影響
-sソースコードのハイライトソースコード漏洩
-d設定値の定義任意コード実行

PHP の CGI モードにおいてオプションの処理(php_getopt)は複数箇所に分かれており、CGI 等の外部からの入力が想定される場合は、オプションの解析処理が無効になります。今回の脆弱性はその判定処理が存在しない一番最初の箇所で発生しています。

まず、脆弱性の対象となる PHP5.4.1 の該当箇所です。外部から渡された引数(argv)をそのまま処理しています。この部分には問題となるオプション -s と -d の処理が含まれています。

=== php-5.4.1/sapi/cgi/cgi_main.c
int main(int argc, char *argv[])
{
  ...
  while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
    switch (c) {
      case 'c':
        if (cgi_sapi_module.php_ini_path_override) {
          free(cgi_sapi_module.php_ini_path_override);
        }
        cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
        break;
      case 'n':
        cgi_sapi_module.php_ini_ignore = 1;
        break;
      case 'd': {
        // 設定値の定義 (コード実行を誘発)
        /* define ini entries on command line */
        int len = strlen(php_optarg);
        char *val;
        ...
      /* if we're started on command line, check to see if
         we are being started as an 'external' fastcgi
         server by accepting a bindpath parameter. */
      case 'b':
        if (!fastcgi) {
          bindpath = strdup(php_optarg);
        }
        break;
      case 's': /* generate highlighted HTML from source */
        // ソースコードのハイライト (ソースコード漏洩を誘発)
        behavior = PHP_MODE_HIGHLIGHT;
        break;
    }
  ...Code language: C++ (cpp)

一方、影響を受けない php-5.1.6-32.el5 は以下の通りです。問題となるオプションに対応する処理自体が存在しません。

=== php-5.1.6/sapi/cgi/cgi_main.c
int main(int argc, char *argv[])
{
  ...
  while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0)) != -1) {
    switch (c) {
      // 対象となる -s や -d の処理自体が存在しない
      case 'c':
        cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
        break;
      case 'n':
        cgi_sapi_module.php_ini_ignore = 1;
        break;
#if PHP_FASTCGI
#ifndef PHP_WIN32
      /* if we're started on command line, check to see if
         we are being started as an 'external' fastcgi
         server by accepting a bindpath parameter. */
      case 'b':
        if (!fastcgi) {
          bindpath = strdup(php_optarg);
        }
        break;
#endif
#endif
    }
   ...Code language: C++ (cpp)

上記の違いにより php-5.1.6-32.el5 は影響を受けないと考えられます。パッチの当たっていない PHP バージョン 5.1.6 についても該当箇所は同様です。入力値の扱いは今回の脆弱性修正まで変わっていないので、影響は -d オプション処理の有無次第です。

問題となる処理は2006年2月頃に PHP レポジトリのリビジョン207052にて追加されています。該当リビジョンの修正一覧と差分は以下です。
Revision 207052
Diff of /php/php-src/trunk/sapi/cgi/cgi_main.c

PHP のリリースとしては、2006年11月頃リリースされたバージョン 5.2.0 にて変更が取り入れられています。その一つ前は2006年8月頃リリースされたバージョン 5.1.6 ですが、こちらにはその変更は含まれていません。脆弱性の影響についてもこの変更に同期しており、バージョン 5.1.6 においては影響を受けず、バージョン 5.2.0 において影響を受ける事を確認しています。

この適用より更に前の2004年にリリースされているバージョン 5.0.0 の時点で、オプションの処理は既に複数に分かれており、CGI 等の外部入力を考慮した実装になっていました。その部分の実装については、今回の脆弱性の影響を受けないバージョンと殆ど変わっていません。今回の問題はその設計の意図を読み取れずに、変更を適用してしまったため発生したのではないかと推測されます。

シェアする