【ディレクトリ列挙編】リンクされてない隠しエンドポイントを掘る|CTF思考フレームワーク R04
こんにちは、アンペンです!偵察ルートR04はディレクトリ列挙編。トップページからリンクをたどっても出てこない『隠しエンドポイント』を、辞書を使った総当たりで掘り出す技を扱います。指紋(R03)で当たりを付けたサーバの内部構造を、さらに一段深く読む回です。
Webサイトには、トップページからリンクをたどっても絶対にたどり着けない“裏ページ”が、実はたくさん眠っています。開発中の管理画面、消し忘れたバックアップ、テスト用のAPI——表に出していないだけで、サーバの上にはちゃんと置いてある。リンクが無いから安全、と思いがちですが、URLを直接叩けば応答してしまうんです。今日は、その“隠し扉”を辞書で炙り出す技と、その塞ぎ方を見ていきましょう。

リンクが無いページって、どうやって見つけるの?

『ありそうな名前』を辞書で総当たりするんだ。/admin /backup.zip /.git/…候補を片っ端から投げて、サーバの反応の差から実在を当てる。ffufやgobusterが定番だよ。
ディレクトリ列挙(Content Discovery)とは、リンクされていない隠しパスをワードリストで総当たりし、HTTPステータスコードの差から実在を炙り出す偵察。鍵は(1)良いワードリスト(SecLists)、(2)ステータスの読み分け(200/301/403が宝)、(3)拡張子総当たり(.bak/.zip/.old)、(4)見つけた階層の再帰探索の4点。とくに403(あるのに拒否)は「隠したい何かがそこにある」サインで最優先。代表ツールはffufgobusterferoxbuster。守る側の鉄則は『不要ファイルを公開領域に置かない+ディレクトリ一覧無効化+大量404をログ検知』。
この記事で分かること
- ディレクトリ列挙が「リンクなし」でも成立する仕組み
- ステータスコードの読み分け(なぜ403が一番おいしいのか)
- ffuf/gobuster/feroxbufferの役割分担とワードリスト選び
- 拡張子総当たり・再帰探索・.git/.envの定番事故
- 守る側の「隠しパスを漏らさない」チェックリスト
📖 はじめてのWebセキュリティR04|ディレクトリ列挙編
リンクされていない隠しエンドポイントを掘り出す偵察の作法。 シリーズ一覧を見る →
⚠️ 大事なお約束
他者サイトへのディレクトリ列挙は、たとえ閲覧目的でも大量アクセスによる業務妨害・不正アクセスとみなされ違法になり得ます。本記事の手順はCTF・自分が運用するサイト・書面で許可された対象でのみ実施してください。
なぜ「リンクなし」でも見つかるのか
Webサイトのページは、トップから辿れるリンクの集合(=サイトマップ)だけではありません。開発中の管理画面、消し忘れたバックアップ、設定ファイル、テスト用APIなど、「公開する気はないがサーバ上には置いてある」ファイルが大量にあります。これらはリンクされていないので普通には見えませんが、URLを直接叩けば応答します。ディレクトリ列挙は、その「ありそうな名前」を機械的に総当たりして実在を当てる手法です。
判定の生命線がHTTPステータスコードです。存在しなければ404、あれば200、移動なら301/302、認証が要れば401、そして「あるけど見せない」が403。この差を読み分けることで、画面を一切見なくても「そこに何があるか」の地図が描けます。
ここで効いてくるのが“サーバの表情”です。人は嘘をついても表情に出ますよね。サーバも同じで、存在しないパスには『404(ありません)』、あるけど見せたくないものには『403(立入禁止)』と、つい違う反応を返してしまう。攻撃者はこの“表情の差”だけを頼りに、中身を一切見ずに『どこに何があるか』を当てていきます。とくに403は、本人が隠したがっている証拠なので、いちばん注目されるわけです。

ここで覚える用語:ファジング(Fuzzing)とワードリスト
意味:ファジングとは、大量の候補を機械的に投げてサーバ応答の差から「当たり」を炙り出す汎用手法。FUZZというキーワードを置いた位置に、ワードリスト(辞書ファイル)の各行を順に当てはめて試します。
例:ffuf -u https://site/FUZZ -w wordlist.txt なら、/admin /login /backup…を上から順に試行。
使いどころ:ディレクトリ・ファイル名・URLパラメータ・サブドメインなど「総当たりで列挙したい全て」に応用できます。定番辞書はSecListsのraft-*やdirectory-list-2.3。
ステータスコードの読み分け(403が宝)
- 200 OK:そのパスは実在し、中身も見られる。最優先で内容確認
- 301/302 リダイレクト:移動先がある=存在の証拠。
/admin→/admin/のような誘導も拾う - 403 Forbidden:「あるのに見せない」=隠したい何かがそこにあるサイン。最もおいしい。別経路・別メソッド・パストラバーサルで回り込めないか検討
- 401 Unauthorized:認証が必要=重要機能の入口。資格情報やデフォルトパスを検討
- 404 Not Found:無し。ただし「全部200を返すソフト404」に注意(後述のフィルタが必要)
- 500 Internal Server Error:叩いた瞬間に落ちる=処理が走っている証拠。パラメータ次第で脆弱性の入口
図解:列挙からお宝発見までの流れ
ワードリスト投入→ステータスで篩(ふるい)分け→200/301/403を深掘り→拡張子総当たり→見つけた階層を再帰探索、という流れで内部構造の地図が完成します。
ホテルの案内図には客室番号しか載っていませんが、廊下には「清掃用具室」「電気室」「関係者通路」といった案内外のドアがいくつもあります。案内図(=リンク)を見るだけでは気づきませんが、廊下を歩いてドアノブを片っ端から回せば「ここは施錠されていない(200)」「ここは『関係者以外立入禁止』の札がある(403)」と分かってしまう。ディレクトリ列挙はまさにこれで、札がかかっているドアほど『中に大事なものがある』と教えてくれるのです。

ツールの役割分担とコツ
- ffuf:高速・柔軟。
FUZZ位置を自由に置けるのでパラメータやHost列挙にも転用可。フィルタ(-fc/-fs/-fw)が強力 - gobuster:シンプルで堅牢。
dir(ディレクトリ)/dns(サブドメイン)/vhostモードを持つ - feroxbuster:自動再帰探索が得意。見つけたディレクトリの奥を勝手に掘り進む
- dirsearch:拡張子総当たりとレポート整形が手軽。初学者に優しい
ツールはどれも似ていますが、ざっくり『ffufは万能でフィルタが強い、feroxbusterは奥まで勝手に掘ってくれる、gobuster/dirsearchは手軽』と覚えておけば十分です。最初の1本はffufかdirsearchで始め、ディレクトリがいくつか見つかったらferoxbusterで再帰探索に切り替える——この流れが効率的。道具より大事なのは“良い辞書”なので、まずSecListsを用意するのが先決ですよ。
ここで覚える用語:ソフト404とフィルタリング
意味:「存在しないパスにも200と独自エラーページを返す」設定をソフト404と呼びます。これがあると全部が当たりに見えて列挙が機能しません。そこで応答サイズ・行数・単語数でフィルタします。
例:存在しないパスが常に4242バイトなら ffuf ... -fs 4242 でそのサイズを除外。
使いどころ:列挙の最初に「絶対に無いパス(例:/zzz_notexist_123)」を1回叩いて基準サイズを測り、それを-fs/-fwに渡すのが定石です。
CTFでやってみよう:自サイトを列挙する
自分が運用するサイトの「隠し扉」を自分で点検する
攻撃者が最初の数分でやる列挙を、自分のサイトに対して先に実行して棚卸しします。各ステップに「なぜやるか」を添えました。
- SecListsから
raft-medium-directories.txtを用意 → なぜ:実績ある辞書が精度と速度を両立するから - 基準測定:
curl -s -o /dev/null -w "%{size_download}" https://yoursite/zzz_notexist→ なぜ:ソフト404のサイズを掴みフィルタに使うため ffuf -u https://yoursite/FUZZ -w 辞書 -mc 200,301,302,401,403 -fs 基準値→ なぜ:意味あるステータスだけ拾い偽陽性を消すため- 拡張子総当たり
-e .php,.bak,.zip,.old,.txt,.json→ なぜ:消し忘れたバックアップ/設定が最大の宝庫だから - 見つかったディレクトリを
feroxbusterで再帰探索 → なぜ:階層の奥に本命(管理API等)があるから /.git//.env/backup.zip/config.php.bakを個別確認 → なぜ:最頻出の情報漏えい事故だから-rateと-tでレート制御し、サーバのアクセスログを観察 → なぜ:本番では過負荷・検知になるので加減を体感するため- 検出した403/401を重点的に記録 → なぜ:「存在するのに隠したい」=価値が高いから
守る側:隠しパスを漏らさない
守る発想は「置かない・出さない・気づく」。そもそも公開領域に余計なファイルを置かず、構造を出さず、列挙の兆候に気づく。具体策を並べます。
- ディレクトリ一覧の無効化:Apache
Options -Indexes/Nginxautoindex off; - .git/.svn/.env を公開領域に置かない+アクセス拒否ルール(漏れれば全ソース流出)
- バックアップ/一時ファイル排除:
.bak .old ~ .zip .swpをWebルートから除去 - 存在を隠すなら一律404:403で「ある」と教えず、秘匿対象は404相当で返す
- WAF/レート制限:同一IPの短時間大量404/403をブロック
- 管理画面はパス秘匿に頼らない:認証+IP制限+多要素を必須に
- ログ監視+アラート:大量404/403はディレクトリ列挙の典型サイン
- robots.txtに秘密パスを書かない:かえって地図を渡すことになる


403を返すと逆に「ある」ってバレるのか…!

そう、存在を隠したいなら『無いふり(404)』が正解。次はR05、Google Dork。一度もサイトを叩かずに、検索エンジン経由で隠しファイルを見つける受動偵察だよ。
まとめ:『辞書×ステータス×再帰』
- 列挙はワードリスト総当たり×ステータスの読み分けで成立する
- 403/401は宝(あるのに隠したい=価値が高い)
- ソフト404はサイズ/語数フィルタで除外する
- 拡張子総当たりと再帰探索でバックアップ・奥の階層を掘る
- 守りは置かない・出さない(一律404)・気づく(ログ検知)
今日の持ち帰りは『403は“隠す”どころか“ここにあるよ”の看板』。存在を伏せたいなら、中途半端に拒否するより、いっそ“無いふり(404)”をするのが正解です。そして根本は、見られて困るものを公開領域に置かないこと。.gitやバックアップzipの置き忘れは、今もっとも多い情報漏えい事故。攻撃者と同じ辞書で、一度自分のサイトを叩いてみてください。
次はR05、Google Dork編。サーバを一切叩かずに検索エンジン経由で隠し情報を拾う、究極の受動偵察を扱います。
