「フラグは目の前なのに、絶対パスが使えない…!」
CTF (Capture The Flag) の世界に足を踏み入れたあなたが、壁にぶつかっているなら、この記事はまさにあなたのために書かれました。特にWeb系の問題で遭遇する「絶対パスフィルタリング」の壁。どうすれば乗り越えられるのでしょうか?
例題を見てみましょう。これは典型的なシナリオです:
“Can you get the flag?”We know that the website files live in /usr/share/nginx/html/
and the flag is at /flag.txt
but the website is filtering absolute file paths. Can you get past the filter to read the flag?
ウェブサイトの公開ディレクトリは /usr/share/nginx/html/
。目指すフラグはルートディレクトリ直下の /flag.txt
。しかし、サーバーは /
から始まる絶対パスを受け付けてくれない…。一見、絶望的に思えるこの状況、あなたならどうしますか?
諦めないでください!この難問を解く魔法の鍵、それは**「相対パス」と、その背後にある「Path Traversal (ディレクトリトラバーサル)」**という仕組みを理解することにあります。
この記事では、
- なぜ絶対パスがブロックされるのか?
- サーバーは具体的にどうやってそれを防いでいるのか?
- 魔法の呪文
../../../../flag.txt
はなぜ効くのか? - そして最も重要な、このPath Traversal脆弱性からあなたのシステムを守る鉄壁の方法
これらを、CTF初心者からセキュリティを学び始めた方まで、誰もが深く理解できるよう、ステップ・バイ・ステップで、徹底的に、そして分かりやすく解説します。読み終える頃には、あなたは Path Traversal を見抜き、対策できる知識を身につけているはずです!
この記事でマスターできること
- CTF問題「Can you get the flag?」の鮮やかな解き方とその思考プロセス
- 絶対パスと相対パスの根本的な違いと使い分け
- ウェブサイトが絶対パスを恐れる理由(セキュリティリスクの本質)
- サーバー側の絶対パス・フィルタリング手法とその(時として存在する)抜け穴
- Path Traversal 攻撃の巧妙な仕組み
../../../../flag.txt
が意味するもの:階層を遡るテクニック- 【最重要】Path Traversal 脆弱性に対する具体的かつ実践的な防御戦略(多層防御)
なぜ絶対パスは”悪者”扱い? ウェブサイトのセキュリティ事情
ウェブアプリケーションの世界では、なぜ /etc/passwd
のような絶対パスが目の敵にされるのでしょうか? 答えはシンプル、それが重大なセキュリティリスクに直結するからです。
絶対パスは、システムのファイル構造の頂点 (/
) から目的地までの完全な住所です。もしユーザーがウェブサイトの入力欄(検索窓、ファイルアップロード、パラメータなど)を通して自由に絶対パスを指定できたら、悪意のあるユーザーは以下のような行動が可能になります。
- 機密情報の窃取:
/etc/shadow
(パスワードハッシュ) やデータベース設定ファイル、秘密鍵など、本来アクセスされるべきでないファイルを盗み見る。 - システム情報の暴露: ログファイル (
/var/log/
) や設定ファイル (/etc/nginx/conf.d/default.conf
など) を読み取り、サーバー構成や他の脆弱性のヒントを得る。 - 不正な操作: アプリケーションの権限設定によっては、重要なファイルの改ざんや削除、あるいは不正なスクリプトの設置につながる可能性すらあります。
こうした危険を防ぐため、多くのまともなウェブサイトやアプリケーションは、ユーザーからの入力に含まれる絶対パスの兆候(/
で始まる、Windowsなら C:\
など)を検出し、処理を拒否するフィルターを備えているのです。今回のCTF問題も、このフィルターの存在がゲームのルールとなっています。
サーバーは絶対パスをどうやって防いでいる? フィルターの裏側
では、サーバー側は具体的にどのようにしてこのフィルターを実現しているのでしょうか? フィルターは、アプリケーションのコード内、Webサーバーの設定、あるいは専用のセキュリティ機器 (WAF) など、様々な場所で実装されます。主な手法を見てみましょう。
- 基本技:単純な文字列チェック(ブラックリスト方式) 最も分かりやすいのは、入力された文字列に「危険なパターン」が含まれていないか単純にチェックする方法です。
- 「文字列が
/
で始まっていたらダメ!」 - 「文字列に
../
が含まれていたらダメ!」 - 「Windows環境なら
C:\
で始まっていたらダメ!」 といったルールです。プログラムで書くと、以下のようなイメージです(これは概念を示す疑似コードです)。
function is_path_suspicious(input_path): if input_path.startswith("/"): return true // 絶対パスは怪しい! if "../" in input_path: return true // 親ディレクトリ移動は怪しい! # ... 他にもチェックルールがあるかも ... return false // たぶん大丈夫だろう
【CTFのヒント】 CTFの問題では、学習目的や難易度調整のために、あえてこの「単純な文字列チェック」だけが実装されていることがよくあります。/
はチェックするけど../
は見逃す、といった実装です。今回の問題のように../../../../flag.txt
が通るのは、まさにこのパターンかもしれません。 - 「文字列が
- 堅実技:パスの正規化と検証 より安全性を高めるには、入力されたパスをOSが解釈する形式に一度**「正規化 (Normalization)」**してから検証します。正規化とは、例えば
images/../docs/file.txt
というパスがあったら、..
を解決してdocs/file.txt
という最終的な形に整理することです。 この正規化後のパスが、ウェブサイトが公開を許可しているディレクトリ(例:/usr/share/nginx/html/
)の範囲内にちゃんと収まっているかを確認します。範囲外ならアクセスを拒否することで、より多くの不正アクセス試行を防げます。 - 推奨技:安全なAPIやフレームワークの機能利用 現代のプログラミング言語(Python, PHP, Javaなど)やウェブフレームワーク(Ruby on Rails, Django, Laravelなど)には、ファイルパスを安全に取り扱うための機能が用意されていることがほとんどです。これらの機能は、内部的に正規化やディレクトリ範囲チェックなどを適切に行ってくれることが多く、開発者はこれらを正しく使うことで、自分で脆弱なコードを書いてしまうリスクを大幅に減らせます。
- 門番技:Web Application Firewall (WAF) による防御 WAFは、ウェブアプリケーションの手前に設置されるセキュリティ専門の壁です。アプリケーションにリクエストが届く前に、その内容をチェックし、「これは典型的な Path Traversal 攻撃だ!」と判断したら、リクエストをブロックしてくれます。SQLインジェクションなど、他の多くの攻撃パターンも検知できます。
フィルターの難しさ: しかし、完璧なフィルターを作るのは至難の業です。攻撃者は ../
を %2e%2e%2f
のようにURLエンコードしたり、....//
のようなトリッキーな文字列を使ったりして、単純なチェックをすり抜けようとします。だからこそ、後述する「多層防御」が重要なのです。
救世主「相対パス」とは? 現在地からの道案内
絶対パスが「完全な住所」なら、相対パスは「ここから見て、あっち」という現在地からの道案内です。ウェブサーバーがファイルを扱うとき、通常、基準となる「現在地」(カレントディレクトリ)が存在します。
相対パスで使う重要な記号は2つだけ覚えましょう。
.
(ドット1つ): 今いる場所 (カレントディレクトリ)..
(ドット2つ): 一つ上の階層 (親ディレクトリ)
あなたが今 /usr/share/nginx/html/
にいると想像してください。
./images/logo.png
は、今いる場所にあるimages
フォルダの中のlogo.png
、つまり/usr/share/nginx/html/images/logo.png
を指します。../
は、一つ上の/usr/share/nginx/
を指します。../../
は、さらにもう一つ上の/usr/share/
を指します。../../../
は、/usr/
を指します。../../../../
は、ついにルートディレクトリ/
に到達します!
この ..
を繰り返すことで、現在の場所からどんどん上の階層へ移動できる、これが相対パスの強力な点です。
なぜ ../../../../flag.txt でフラグが取れるのか? 魔法の解読
さて、核心に迫りましょう。CTF問題で ../../../../flag.txt
がなぜ有効なのか?
- スタート地点: ウェブサーバーはリクエストを処理するとき、多くの場合、ウェブサイトの公開ディレクトリ、つまり問題文にある
/usr/share/nginx/html/
を基準(カレントディレクトリのようなもの)として動作します。 - 階層を登る: 目標の
/flag.txt
はルートディレクトリ直下にあります。スタート地点からルート/
までは、先ほど数えたように/html/
,/nginx/
,/share/
,/usr/
の4つの階層を上がる必要があります。だから..
を4回繰り返す../../../../
を使うのです。 - ゴールを指定: ルート
/
まで登ったら、あとは目的のファイル名flag.txt
をくっつけるだけ。 - フィルター回避: そして最も重要な点。この
../../../../flag.txt
というパスは、/
から始まっていません! そのため、「/
で始まるパスは禁止」という単純な絶対パスフィルターのチェックをすり抜けてしまうのです。フィルターが../
の存在をチェックしていなければ、この相対パスは有効なパスとして処理され、見事/flag.txt
にたどり着けてしまう、というわけです。
これが、Path Traversal 攻撃の基本的な仕組みであり、CTFでしばしば用いられるテクニックです。
【補足】 実は ../
の数は、少し多めでも問題ないことが多いです。例えば ../../../../../flag.txt
としても、ルート /
より上には行けないため、結果的に /flag.txt
を指します。CTFでは試行錯誤も重要です。
Path Traversal (ディレクトリトラバーサル) 攻撃 – その危険性を再認識
今回使ったテクニックは、Path Traversal(パストラバーサル) または Directory Traversal(ディレクトリトラバーサル) と呼ばれる、非常に古くから知られているウェブアプリケーションの脆弱性です。
ユーザーからの入力を適切に**検証・無害化(サニタイズ)**することなくファイルパスの一部として使ってしまうことで、攻撃者は ../
を注入し、ウェブサーバーの公開ディレクトリの外にある、本来アクセスできないはずのファイルやディレクトリにアクセスしようとします。
セキュリティの国際的な専門家集団である OWASP (Open Web Application Security Project) も、この脆弱性を長年、重大なリスクとして警告し続けています。彼らの言葉を借りれば、「ファイルを参照する変数に ../
シーケンスや絶対パスを操作して注入することで、アプリケーションのソースコード、設定ファイル、重要なシステムファイルを含む、ファイルシステム上の任意のファイルやディレクトリにアクセスされる可能性がある」のです。
CTFの問題はこの脆弱性を体験的に学ぶ良い機会ですが、現実世界のシステムでこれが悪用されれば、情報漏洩やシステム侵害に直結する深刻な事態を招きます。
【鉄壁防御マニュアル】Path Traversal からシステムを守る具体的対策
では、どうすればこの厄介な Path Traversal 脆弱性から、あなたの開発するウェブアプリケーションやシステムを守れるのでしょうか? 重要なのは「一つの対策に頼らない」こと。複数の防御壁を築く**「多層防御 (Defense in Depth)」**が鍵となります。
防御層1:心構え – 「ユーザー入力は信用しない」を徹底
全ての対策の基本です。ユーザーが入力するデータ(ファイル名、ID、検索クエリ、URLパラメータなど)は、常に悪意のある操作(../
や想定外の文字の挿入など)が試みられる可能性がある、という前提で設計・実装を進めましょう。
防御層2:入り口対策 – 入力値の厳格な検証と無害化(サニタイズ)
ユーザーからのデータを受け取ったら、それがファイルパスとして安全か、徹底的にチェックします。
- サニタイズとは?: 入力データから危険な文字(
../
,/
,\
, ヌルバイト\0
など)を取り除いたり、無害な形に変換したり(エスケープ)する処理のことです。「消毒」のようなイメージですね。 - ブラックリストの限界を知る: 「
../
が入ってたらダメ」というブラックリスト方式は、前述の通り%2e%2e%2f
のようなエンコードや、巧妙な組み合わせで回避される可能性があります。これだけに頼るのは危険です。 - パス正規化の活用: 入力パスをOSが解釈する最終形に「正規化」してから検証することが有効です。これにより
..
を使ったごまかしを排除できます。多くの言語でos.path.normpath
(Python) やrealpath()
(PHP) のような関数が提供されています。
防御層3:最善策 – 厳格な「ホワイトリスト方式」での検証
これが最も推奨される、強力な防御策です。「許可されたパターン以外は、すべて拒否する」という考え方です。パーティーの招待客リストのように、リストに名前がある(=許可パターンに一致する)入力だけを受け付けます。
- なぜ強力か? 許可するものを明確に定義するため、未知の攻撃パターンや巧妙なバイパス試行に対しても強い防御力を発揮します。
- 実装例:
- ファイル名のパターン制限: 「ファイル名は英数字とアンダースコアのみ、拡張子は
.jpg
か.png
だけ許可」のように、正規表現で厳密に定義する。コード スニペット# 例:英数字とアンダースコア、ハイフンのみ許可、拡張子は jpg, png, pdf ^[a-zA-Z0-9_\-]+\.(jpg|png|pdf)$
- IDベースのアクセス: ユーザーにはファイル名ではなく、安全なID(例:
1
,2
,3
)やキー(例:"user_icon"
,"report_202504"
)を選ばせ、サーバー内部で実際のファイルパスに対応付ける。ユーザーはパス構造に一切触れません。 - ディレクトリ固定+ファイル名検証: ユーザーからはファイル名のみを受け取り、サーバー側で固定の安全なディレクトリ(例:
/var/www/myapp/uploads/
)と結合する。結合後も、最終的なパスが本当にそのディレクトリ内にあるか、正規化して再確認する(二重チェック)。
# ホワイトリスト方式の概念を示すPython風コード例 import os ALLOWED_EXTENSIONS = {".jpg", ".png", ".pdf"} UPLOAD_DIR = "/var/www/myapp/safe_uploads/" # 安全な保管場所 def process_uploaded_file(user_filename_input): # 1. 基本的なサニタイズ (例: 前後の空白除去、ヌルバイト除去) filename = user_filename_input.strip().replace('\0', '') # 2. パストラバーサルに使われる文字がないかチェック if '/' in filename or '\\' in filename or '..' in filename: print("Error: Invalid characters found in filename.") return False # 3. 拡張子をホワイトリストでチェック try: _ , ext = os.path.splitext(filename) if not ext or ext.lower() not in ALLOWED_EXTENSIONS: print("Error: File type not allowed.") return False except ValueError: print("Error: Could not determine file extension.") return False # 4. 安全なベースディレクトリと結合 # ※ user_filename_input はサニタイズ・検証済みのはずだが、念のため final_path = os.path.join(UPLOAD_DIR, filename) # 5. 【重要】正規化し、最終パスが本当にベースディレクトリ内にあるか検証 normalized_path = os.path.normpath(final_path) # normpath(UPLOAD_DIR) でベースパスも正規化して比較する if not normalized_path.startswith(os.path.normpath(UPLOAD_DIR) + os.sep): print("Error: Security check failed (Path normalization).") return False # 6. ここまで通れば安全な可能性が高い print(f"Processing file at: {normalized_path}") # ... ファイル保存などの処理 ... return True # --- 呼び出し --- # process_uploaded_file("image.jpg") -> True (成功するはず) # process_uploaded_file("../../../etc/passwd") -> False (失敗するはず)
(注意: これは概念を示すコードです。実際の利用にはより堅牢なエラー処理やOS間の差異への配慮が必要です) - ファイル名のパターン制限: 「ファイル名は英数字とアンダースコアのみ、拡張子は
防御層4:最後の砦 – 「最小権限の原則」の適用
万が一、上記の防御層が突破されても、被害を最小限に抑えるための保険です。ウェブサーバーやアプリケーションを実行するOSユーザーの権限を、必要最低限に絞ります。
- なぜ有効か? たとえ攻撃者が
/etc/passwd
を読み取ろうとしても、ウェブサーバーの実行ユーザー (例:www-data
) にそのファイルの読み取り権限がなければ、アクセスはOSレベルでブロックされます。書き込み権限がなければ、ファイルを改ざんされることもありません。 - 具体的な設定:
- ウェブサーバー (Apache, Nginx) を絶対に
root
で動かさない。専用の低権限ユーザー (www-data
,nginx
など) を使う。 - ファイルシステムのパーミッションを適切に設定し、ウェブサーバーのユーザーは、公開に必要なファイルと、書き込みが必要な特定のディレクトリ(アップロード用など)以外にはアクセスできないようにする。
- Dockerなどのコンテナ技術で、アプリケーションごとに環境と権限を隔離するのも非常に有効です。
- ウェブサーバー (Apache, Nginx) を絶対に
防御層5:先人の知恵 – 「安全なAPI・フレームワーク」の活用
車輪の再発明は避けましょう。多くの言語やフレームワークは、セキュリティを考慮したファイル操作APIを提供しています。
- なぜ推奨されるか? これらのAPIは、開発者が陥りやすい Path Traversal の罠を回避できるよう設計されていることが多いです。セキュリティ専門家によって作られ、テストされた機能を活用することで、開発者はより安全なコードを効率的に書けます。
- 使い方に注意: ただし、これらのAPIも万能ではありません。使い方を間違えれば(例えば、ユーザー入力を検証せずにそのまま渡すなど)、脆弱性を生む可能性があります。必ず公式ドキュメントを熟読し、セキュリティ上の推奨事項に従ってください。
防御層6:門番の強化 – 「Web Application Firewall (WAF)」の導入
アプリケーションの手前で、不審なリクエストを検知・ブロックするWAFは、多層防御の重要な一部です。
- 役割: 典型的な
../
の連続など、既知の Path Traversal 攻撃パターンを含むリクエストを自動的に遮断してくれます。アプリケーションコードの修正がすぐに行えない場合の、一時的な緩和策としても有効です。 - 過信は禁物: WAFは銀の弾丸ではありません。未知の攻撃や巧妙な回避テクニックには対応できないこともあります。あくまで防御層の一つと捉え、アプリケーション自体のセキュリティ対策(コード修正)と併用することが不可欠です。
結論:多層防御で築く鉄壁のセキュリティ
Path Traversal 脆弱性を完全に防ぐには、**単一の対策に依存するのではなく、これまで見てきた複数の防御策を組み合わせる「多層防御」**のアプローチが不可欠です。
- 意識 (Mindset): ユーザー入力を疑うことから始める。
- 入り口 (Input Validation): ホワイトリスト方式で厳格に検証・サニタイズする。
- 処理 (Secure API): 安全なファイル操作APIを正しく使う。
- 環境 (Least Privilege): 最小権限でプロセスを実行する。
- 前線 (WAF): WAFで既知の攻撃パターンをブロックする。
これらの層を重ねることで、一つの層が突破されても次の層で食い止め、システム全体の安全性を飛躍的に高めることができます。
今回のCTF問題を通して、Path Traversal の仕組みとその対策の重要性を理解していただけたでしょうか。この知識は、CTFでの勝利だけでなく、あなたが将来、安全なウェブサービスを構築し、運用していく上で、必ず役立つ foundational knowledge (基礎知識) となるはずです。
たび友|サイトマップ
関連webアプリ
たび友|サイトマップ:https://tabui-tomo.com/sitemap
索友:https://kentomo.tabui-tomo.com
ピー友:https://pdftomo.tabui-tomo.com
パス友:https://passtomo.tabui-tomo.com
クリプ友:https://cryptomo.tabui-tomo.com