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

第39回 MOPS:静的PHPソースコード脆弱性スキャナ RIPS

この記事を読むのに必要な時間:およそ 6 分

Tokenizer

RIPSのコアとなるTokenizerモジュールを使ったことがある方は少ないのではないでしょうか? 筆者も何年も前にコードカバレッジツールを作成する際に利用したきりです。

Tokenizerの利用は簡単でtoken_get_all関数でファイルの中のPHPトークン(文,変数,関数など)を取得します。トークンは名前でなくID(整数)で保存されているのでtoken_name関数で名前を取得します。トークンのへの分割はPHPに組み込まれている本物のPHPパーサを利用しているのでPHPスクリプトを確実にトークンに分解できます。

<?php
    $a = $ GET['a'];
    system($a, $ret);
?>

をトークンに分解すると以下のようになります。

name: T_OPEN_TAG            value: <?php    line: 1
name: T_VARIABLE            value: $a   line: 2
name: T_WHITESPACE          value:      line: 2
                        =
name: T_WHITESPACE          value:      line: 2
name: T_VARIABLE            value: $_GET    line: 2
                        [
name: T_CONSTANT_ENCAPSED_STRING    value: 'a'  line: 2
                        ]
                        ;
name: T_WHITESPACE          value:      line: 2
name: T_STRING              value: system   line: 3
                        (
name: T_VARIABLE            value: $a   line: 3
                        ,
name: T_WHITESPACE          value:      line: 3
name: T_VARIABLE            value: $ret     line: 3
                        )
                        ;
name: T_WHITESPACE          value:      line: 3
name: T_CLOSE_TAG           value: ?>   line: 4

RIPSはこのようにして取得したトークンから分析に必要なトークンを抽出して処理します。

RIPSが処理するトークン

T_INCLUDEinclude/require文
T_FUNCTION関数(ユーザ定義,システム/モジュール関数,両方)
T_RETURNreturn文
T_VARIABLE変数
T_STRING文字列
T_EXIT, T_THROWプログラムの終了
Curly braces {}制御構造の判定に必要

これらに加えて,extract(), list() や define() など特別なトークンを生成する関数も処理対象となります。論文に記載されていませんでしたが,E_ECHO(echo文)も抽出対象です。

RIPSが各トークンをどのように処理するか,詳しい解説はDahse氏の論文を参照してください。

Web UI

RIPSのWeb UIはシンブルで分かりやすいです。スキャンしたいPHPのスクリプトファイルをWebサーバがアクセスできるディレクトリ(場所はドキュメントルート以外でよい)に置き,⁠path/file」にディレクトリ名をセットして「scan」ボダンを押すだけです。サブディレクトリもスキャンしたい場合は「subdir」チェックボックスをチェックします。

デフォルトではJavaScriptインジェクション(クロスサイトスクリプティング)以外の脆弱性をスキャンします。JavaScriptインジェクション脆弱性のスキャンはfalse-positive(誤って脆弱と検出される)場合が多いからだと考えられます。すべてをレポートさせることも可能です。

図5 Web UI

図5 Web UI

左側のアイコンをクリックするとソースコードや攻撃コードの生成ウィンドウが開きます。攻撃コードの生成ウィンドウはCURLモジュールを使った攻撃用のPHPスクリプトが生成できるようになっていました。筆者が使ったバージョンまたは環境では問題があるようで,完全な攻撃コードは生成できませんでした。しかし,サンプルコードのような明白な脆弱性であれば,ほんの少し手直しすれば実際に攻撃できるコードを生成しました。

RIPSはスキャンする脆弱性のタイプ,verbosity level(脆弱性レポートのレベル⁠⁠,コードのスタイルを選択できるようになっています。Verbosity Levelは以下のようになっています。

RIPSレポートのVerbosity Level

Level1安全性を確保するための関数を呼ばずに,危険な状態でPVF関数を呼び出すケースのみ
Level2include文とデータベースクエリのスキャンも含める
Level3安全性を確保するための関数を呼び出してPVF関数を呼び出すケースも含める
Level4RIPSがスキャンした際の脆弱性以外の情報も含める
Level5すべてのPVF関数呼び出しを含める

Level1の場合はfalse-positive(誤って脆弱性と判断してしまう状態)にならないように調整されいるとしています。Level1の場合,false-nagetive(脆弱性があるのに検出できない状態)は存在して当然と考えるべきでしょう。

一時的なJavaScriptインジェクションを十分に検出するにはLevel2,永続的なJavaScriptインジェクションを検出するにはLevel3に設定する必要があります。当然,false-positiveとなるレポートはレベルが上がるにつれて増えてきます。Level4以上では情報レベル,つまり脆弱性情報ではないレポートも含まれています。

実際のアプリケーションのスキャン

論文にはセキュリティ研修用の脆弱性があるプログラムのスキャン結果が載っていました。実際のアプリケーションではどのような結果になるのか,いくつかのPHPアプリケーションをsourceforge.netからダウンロードしてきてスキャンし,脆弱性が検出できたアプリケーションを紹介します。明示していない限り筆者はソースコード中味を確認していません。

修正されるかどう分かりませんが,作者へ脆弱性情報は連絡済みです。

WebFileBrowser

図6 WebFileBrowser

図6 WebFileBrowser

Verbosity level 1で的確に脆弱なコードを検出しました。このアプリケーションは非常に古いアプリケーションらしく,move_uploaded_file関数を使用せず,copy関数でアップロードされたファイルをコピー,unlink関数で削除しています。このようなコードはファイルを不正にアップロードされたり,削除される場合があります。このようなコードは書いてはいけません。

必ずしも危険な訳ではありませんが,extract関数によって$HTTP_POST_VARSがスコープに取り入れられていることも警告されています。先程の脆弱なコードをこの脆弱性を生む可能性が高いコードによって,実際にファイルアップロード攻撃が行える可能性が高い事が分かりました。

図7 WebFileBrowser - JavaScriptインジェクション

図7 WebFileBrowser - JavaScriptインジェクション

RIPSが検出可能なJavaScriptインジェクションをすべてレポートさせるにはVerbosity level 3以上が必要と書かれていましたが,Verbosity level 1でもかなり検出(30)しました。Level1で多数レポートされたことも原因ですが,Level3に変えても脆弱性の追加はわずか(+5)でした。最近のブラウザはHTTPのLocationヘッダによるJavaScriptインジェクションは行えなくなっているのですが,これも脆弱性としてレポートされます(JavaScriptインジェクションが実行できなくても,リダイレクトを制御されるのはセキュリティ上好ましくないのどちらにしても修正すべき⁠⁠。

SimpleQuiz

図8 SimpleQuiz

図8 SimpleQuiz

このアプリケーションもVerbosity level 1で的確に脆弱なコードを検出しました。今回は典型的なSQLインジェクション脆弱性です。mysql_real_escape_string関数でエスケープすべきパラメータがエスケープされていません。sqlmapなどのツールによって簡単にアクセス可能なデータベースのデータすべてを盗めます(直接インターネットに接続されている場合,可能性ではなくほぼ確実に盗れることがこのレポートだけで判断できます⁠⁠。

図9 SimpleQuiz - JavaScriptインジェクション

図9 SimpleQuiz - JavaScriptインジェクション

このアプリケーションでもLocationヘッダの不具合を2つ検出しました。Verbosity Level 1から3までは検出されるJavaScriptインジェクションの数には変化がありませんでした。

phpMyInventory

図10 phpMyInventory

図10 phpMyInventory

このアプリケーションは2003年からメンテナンスされておらず,register_globals=onが前提となっているレガシーアプリケーションです。このコードは古かったので中味を見てみました。SQLインジェクションの塊と言えるコードでした。Verbosity level 1では何らかの不具合でまったく問題を検出できませんでした。スクリーンショットはLevel 2のスナップショットです。

流石にregister_globals=onのコードに対応していないと思われるレポート結果でした。RIPSは最近のアプリケーションなのでregister_globals=onが前提のアプリケーションの脆弱性が検出できなくてもバグとは言えないでしょう。古いアプリケーションのスキャンにはRIPSは使用できないと考えたほうがよいでしょう。

JavaScriptインジェクションの検出もVerbosity level 1では何らかのエラーで脆弱性が検出できないようでした。Level 2,Level 3では多くのJavaScriptインジェクション脆弱性がレポートされました。これらの多くは実際に攻撃可能なJavaScriptインジェクション脆弱性です。

まとめ

RIPSはアプリケーションのセキュリティ状態の概要を見るには非常に有用なアプリケーションです。セキュアなPHPアプリケーション開発や受け入れ検査の最低ラインとして利用するのもよいでしょう。ただし,ソースコードを分析するタイプのスキャナには必ず,false-positive,false-negativeの問題があります。間違って脆弱性として検出されたり,されなかったりします。

また,最近のWebアプリケーションはWebアプリケーション開発フレームワークを利用したMVCモデルで構築されることが多いと思います。今回はMVCモデルのアプリケーションのスキャンは試しませんでしたが,恐らくfalse-negativeとなるケースが増えると予想しています。MVCモデルの場合,多くのオブジェクトとスクリプトファイルが使用されているため,変数のトラッキングが不正確になってしまうのではないかと考えています。

いずれにせよPHPアプリケーションのセキュリティ状態の概要を把握するには便利なツールであることには変わりありません。インストールはPHPをインストールしたWebサーバに展開するだけです。一度自分が開発したアプリケーションや使用中のアプリケーションをスキャンしてみてはいかがでしょうか?

Dahse氏が参考文献としてあげたURL

1.Fabien Coelho, PHP-related vulnerabilities on the National Vulnerability Database
http://www.coelho.net/php_cve.html
2.The PHP Group, Predefined Variables
http://www.php.net/manual/en/reserved.variables.php
3.The PHP Group, system - Execute an external program and display the output
http://www.php.net/system
4.The PHP Group, escapeshellarg - Escape a string to be used as a shell argument
http://www.php.net/escapeshellarg
5.The PHP Group, token get all - Split given source into PHP tokens
http://www.php.net/token-get-all
6.The PHP Group, token name - Get the symbolic name of a given PHP token
http://www.php.net/token-name
7.The PHP Group, Execution Operators
http://php.net/manual/en/language.operators.execution.php
8.The PHP Group, Control Structures
http://php.net/manual/en/language.control-structures.php
9.The PHP Group, List of Parser Tokens
http://php.net/manual/en/tokens.php
10.Nenad Jovanovic, Christopher Kruegel, Engin Kirda, Pixy: A Static Analysis Tool
for Detecting Web Application Vulnerabilities (Short Paper)
http://www.seclab.tuwien.ac.at/papers/pixy.pdf
11.Nenad Jovanovic, Christopher Kruegel, Engin Kirda, Pixy: A Static Analysis Tool
for Detecting Web Application Vulnerabilities (Technical Report)
http://www.seclab.tuwien.ac.at/papers/pixy_techreport.pdf
12.Nenad Jovanovic, Christopher Kruegel, Engin Kirda, Precise Alias Analysis for
Static Detection of Web Application Vulnerabilities
http://www.iseclab.org/papers/pixy2.pdf
13.Davide Balzarotti, Marco Cova, Vika Felmetsger, Nenad Jovanovic, Engin Kirda,
Christopher Kruegel, Giovanni Vigna, Saner: Composing Static and Dynamic Analysis to Validate Sanitization inWeb Applications
http://www.iseclab.org/papers/oakland-saner.pdf
14.OWASP, OWASP SWAAT Project
http://www.owasp.org/index.php/Category:OWASP_SWAAT_Project

著者プロフィール

大垣靖男(おおがきやすお)

University of Denver卒。同校にてコンピュータサイエンスとビジネスを学ぶ。株式会社シーエーシーを経て,エレクトロニック・サービス・イニシアチブ有限会社を設立。
オープンソース製品は比較的古くから利用し,Linuxは0.9xのころから利用している。オープンソースシステム開発への参加はエレクトロニック・サービス・イニシアチブ設立後から。PHPプロジェクトでは,PostgreSQLモジュールのメンテナンスを担当している。

URLhttp://blog.ohgaki.net/

著書