1. 漏洞概述
目录遍历漏洞(Directory Traversal),也常被称为路径遍历(Path Traversal)。当应用程序接收用户输入的参数来决定读取哪个文件时,如果没有对用户输入进行严格的安全过滤,攻击者就可以通过输入特殊的目录跳转字符(如 ../ 或 ..\),跳出程序预设的目录限制。
攻击者利用这个漏洞,可以访问系统上的任意文件,读取敏感信息(如系统密码文件 /etc/passwd、配置文件、源代码等),如果结合其他漏洞甚至可能导致服务器被完全控制。
2. 闯关实操 (Exploitation)
第一步:页面功能观察与参数发现
打开关卡页面,主界面上显示了两篇小短文的链接:
we're jarheads!Truman's word!
我们点击第一个链接,观察浏览器顶部地址栏的变化。发现 URL 变为了:http://localhost:8899/vul/dir/dir_list.php?title=jarheads.php
这里出现了一个非常明显的传参点:title=jarheads.php。系统似乎是通过 title 这个参数来决定页面展示哪个 PHP 文件的内容。
第二步:Payload 构造与测试
既然 title 接收的是一个文件名,我们自然会想到:如果改变 title 后面的变量值,尝试使用相对路径去访问其他目录的文件,系统会作何反应?
我们可以使用典型的 Linux 敏感文件路径 /etc/passwd 来进行测试。为了跳出当前的 soup/ 目录,我们需要在文件名前加上多个 ../(返回上一级目录)。
我们使用 HackBar 或直接修改 URL,将参数修改为:title=../../../../etc/passwd
(注:../ 的数量取决于当前文件所在的目录深度,多写几个通常没有影响,因为到达根目录 / 后继续 ../ 依然是根目录。)
第三步:漏洞利用成功
敲击回车发送请求后,页面上赫然打印出了 Linux 系统的用户账号信息(如 root:x:0:0:root:/root:/bin/bash 等)。这证明了目录遍历漏洞确实存在,我们成功跨越了系统目录读取了敏感数据!

3. 源码分析 (Code Review)
为什么这么轻易就被遍历了?我们来看一下后端的 PHP 源码:
$html='';
if(isset($_GET['title'])){$filename=$_GET['title'];
// 这里直接把传进来的内容进行了 require(), 造成问题
require "soup/$filename";
// echo $html;
}
漏洞成因非常直接:
- 程序通过
$_GET['title']获取用户输入,并直接赋值给变量$filename。 - 毫无防备 地将拼接后的路径
"soup/$filename"直接交给了 PHP 的require函数执行。 - 程序没有对
$filename进行任何过滤,没有检查里面是否包含../等非法字符,也没有限制文件包含的路径范围。
当我们将 $filename 传入 ../../../../etc/passwd 时,底层执行的其实是:require "soup/../../../../etc/passwd";
这就相当于直接 require "/etc/passwd",从而把文件内容直接输出到了前端页面。
(注意:这里使用的是 require,它不仅能读取文件内容,如果读取的文件中包含 PHP 代码,还会当作 PHP 代码执行,所以这里本质上也构成了一个本地文件包含漏洞 LFI)。
4. 修复建议
防御目录遍历漏洞的核心思路是:永远不要信任用户的输入,并且切断用户输入与文件系统底层 API 的直接交互。
针对本关卡的修复建议如下:
- 使用白名单机制(推荐)
如果能够确定用户可以访问的文件列表,最好的办法是写死一个数组映射。比如:传入id=1,后端代码对应包含jarheads.php,传入其他值一律拒绝。避免用户直接输入文件名。 - 过滤危险字符
如果一定要传递文件名,必须过滤掉.和/、\等目录跳转字符。 - 使用
basename()函数basename()函数会截取路径中的最后一部分(即纯文件名),从而彻底消除掉../这种路径穿越企图。// 修复示例 if(isset($_GET['title'])){// 使用 basename 提取纯文件名,丢弃任何路径信息 $filename = basename($_GET['title']); $filepath = "soup/" . $filename;// 最好再加一层文件是否存在的判断 if(file_exists($filepath)){require $filepath;} else {echo "文件不存在!";}}