资讯动态

目录遍历漏洞详解:从路径穿越原理到检测修复实战

发布时间:2026/9/28 12:48:52 来源:尧图企业网站定制
有一次做授权测试目标是一个内部管理系统的文件下载功能。下载接口长这样/download?filereport_2024.pdf我顺手把file参数改成../../../etc/passwd结果浏览器直接返回了 passwd 文件内容。那一次测试前后没超过三分钟。这就是目录遍历漏洞也叫 Path Traversal、任意文件读取或路径穿越。它常年挂在 OWASP Top 10 里多数时候归在越权访问或安全配置不当一类。很多人觉得它“简单到不值一提”但实际渗透中它往往是撬开内网的第一根杠杆。这篇文不打算堆高大上的理论就讲讲它的成因、绕过思路、怎么测、怎么修以及我在实际项目里踩过的那些坑。适合刚入门的安全新手也适合写代码写到一半被逼着自查漏洞的开发同学运维同事务必看看修复部分。1. 目录遍历漏洞的成因与本质1.1 从“路径拼接”说起先看一个典型的 PHP 老代码?php $file $_GET[file]; $path /var/www/downloads/ . $file; header(Content-Type: application/octet-stream); readfile($path); ?这段代码的问题一眼就能看出来$file完全由用户控制程序拿它和基础目录拼接出一个完整路径然后直接 readfile。当你传../../../../etc/passwd实际拼出来的是/var/www/downloads/../../../../etc/passwd系统解析路径时会一路向上跳最终落在/etc/passwd上文件内容就出来了。这类问题的本质是信任了不该信任的输入。开发者的本意是“只能访问下载目录里的文件”但没有在代码层面落实这个限制。操作系统本身并不觉得这有什么问题——/var/www/downloads/../../../etc/passwd就是一个合法的绝对路径系统会正常执行。换句话说漏洞不是你传了一个带..的字符串造成的而是程序对传入字符串缺少约束校验造成的。在 OWASP Top 10 里目录遍历通常被归到 A01:2021-Broken Access Control越权访问或者 A05:2021-Security Misconfiguration安全配置不当。我更倾向于把它理解为一种“访问控制失效”应用需要限制用户能访问的资源范围但没有做到。1.2 为什么黑名单过滤总是“防不胜防”很多开发同学第一反应是把..过滤掉不就行了理论上可以但实践里几乎每次都翻车原因在于过滤规则永远追不上解析差异。同一段../在不同场景下可以打扮成各种形态原始形式说明../../etc/passwd经典 Unix 路径穿越..\..\windows\win.iniWindows 路径穿越反斜杠%2e%2e%2fetc/passwdURL 编码%2e是点%2f是斜杠%252e%252e%252fetc/passwd双重编码服务器和框架各解一次码....//etc/passwd部分正则只过滤../....//中间被系统解析成..//etc/passwd直接传绝对路径不看基础目录..%2f..%2f..%2fetc/passwd斜杠编码点不编码%c0%ae%c0%ae/早期 IIS 的 Unicode 编码绕过现在已经很少见这里的关键点在于“解码次数”。Web 请求从客户端到服务器再到应用框架每一层都可能做一次 URL 解码。如果开发者只在自己这一层过滤一次../那双重编码的 payload 就能穿透。更麻烦的是过滤../不一定能挡住..\Windows 服务器上反斜杠也是合法路径分隔符。有些正则考虑到了反斜杠但漏了 URL 编码后的斜杠。我做过一个比喻这就像小区门口只设了一道门禁保安只认一种工牌但攻击者手里的工牌有无数种材质、颜色、印刷方式。黑名单的宿命就是如此——你列出的永远是一份有限清单而攻击者的变形方式几乎是无限的。所以真正靠谱的修复方案从来不是“过滤”而是“验证”后面第四节会详细说。2. 目录遍历漏洞能造成多大影响2.1 从 /etc/passwd 到数据库配置目录遍历最直接的影响就是任意文件读取。攻击者可以顺着路径一路往上跳读取服务器上的敏感文件。按我在项目里的经验下面这些文件优先级最高/etc/passwd确认系统用户和路径结构存在性最好的探测目标。/etc/shadow一般读取不到权限受限但值得一试。/var/www/html/config.php、.env、application.yml、web.config数据库账号、API 密钥、密钥串全在里面。应用源码文件拿到源码后做白盒审计往往能发现第二个漏洞。/root/.ssh/id_rsa如果应用运行权限足够高私钥直接泄露。日志文件Nginx/Apache 访问日志、应用日志有时包含管理员会话或者 SQL 语句。举一个实际场景。有一次测试电商系统前端有一个导出订单的接口参数是?exportorder_20240101.csv。我把参数改成../../../../var/www/html/config.php返回的是 PHP 源码。在源码里发现了数据库地址、账号密码还是 root 权限。虽然服务器数据库没对公网开放但当时的内网里这台数据库是很多系统的共用的。顺着这份配置后面又打了两台机器。一个目录遍历生生变成了内网横向的起点。单纯“读文件”已经够严重但更怕的是这个漏洞出现在一些特殊接口上。比如文件下载接口存在路径穿越同时服务器上又有用户上传的文件那攻击者可以把/etc/crontab读走再比如读到了备份文件/var/backups/backup.tar.gz里面可能打包了整个站点的源码和数据库那就不是“读一个文件”了是“端走一整个系统”。2.2 不止于“读文件”目录遍历常常不是孤立存在的它的杀伤力经常通过组合其他漏洞体现。第一类组合是文件上传 路径穿越。现在的上传功能普遍限制后缀名比如只允许 jpg、png脚本文件传不上去。但如果上传的文件名可控而且服务器是 Apache 或者 Nginx攻击者可以构造shell.php%00.jpg之类的手法虽然空字节截断在现在的主流环境不好用了更常见的是利用路径穿越覆盖目录上传接口的保存路径写成uploads/加一个../../shell.php的文件名如果后端没有对文件名做过滤文件会被写到网站根目录下配合 Apache 多后缀解析就有机会执行。热词里提到的“后端正则限制了很多后缀但是服务器是 Apache2”说的就是这类场景。第二类组合是文件包含 目录遍历。本地文件包含LFI很多情况下就是目录遍历的升级版——前者只是把文件读出来后者把文件当成代码执行。如果应用里有include($_GET[page])这类写法配合路径穿越可以直接包含/etc/passwd或者日志文件讲究一点还能通过包含日志写入 PHP 代码完成 RCE。第三类组合是框架/中间件自身的历史 CVE。老 PHP 框架出现过路径处理缺陷Java 中间件也有过路径穿越类型的漏洞。这类问题本质上还是同一个根因用户输入没有经过充分校验就拼进了路径操作。2024 年曝出的某几个 PHP 框架文件上传类漏洞分析到最后还是绕过了路径过滤逻辑。所以目录遍历的 CVSS 评分往往在中高危区间但实际利用里它能串起的链路远比自己单独的评分更高。碰到这种漏洞不要写一句“可读取任意文件建议修复”就完事一定要顺着文件内容继续往下想读到了什么还能做什么这是漏洞报告中真正有价值的部分。3. 如何系统性地检测目录遍历漏洞3.1 手工测试的“套路”手工测目录遍历核心是“找参数、改参数、看响应”三步但每一步都有细节。先找参数。目录遍历漏洞一般出现在带路径的 GET 参数上常见参数名有file、path、filename、download、img、url、page、route、template、doc。凡是后端可能拿这个参数拼文件路径的都值得测。POST 请求体的参数同样要测尤其是一些 JSON 接口字段名如filePath、fileName都可能成为突破口。热点里提到的“phpcookies漏洞”意思是不要忽略 Cookie 里的路径类字段有些应用会把用户身份、语言、皮肤主题存在 Cookie 里取值后拼路径读取资源同样可能存在穿越。拿到参数后先请求一个正常的文件比如filereadme.txt观察正常响应是什么样状态码、Content-Type、响应体大小、缓存头记录下来当基线。然后再用穿越 payload 替换比如file../../../etc/passwd。怎么判断有没有穿越成功响应体里出现root:x:0:0:之类的 passwd 内容铁定中招。响应状态码从 200 变成 403 或 500说明文件存在但权限不够或者路径被拼错了需要调层级。响应体是 PHP 报错信息显示了路径拼接后的完整路径这本身也是信息泄露能帮你调整穿越层数。响应时间异常比如某些日志文件很大读取慢也是信号。层级数量是个常见问题。服务器上站点根目录通常是/var/www/htmlWeb 应用项目目录可能再深几层比如/var/www/html/application/controllers。穿到系统根目录../../../../etc/passwd一般是四个层级但不确定时就多试几组../加 3 到 8 层或者直接传绝对路径/etc/passwd。很多应用在拼接路径时根本不关心前缀是什么绝对路径往往一击即中。手工测试时我习惯先测 3 到 5 个关键参数每个参数跑一组常见 payload而不是对着单个参数无穷无尽地试。效率和覆盖率之间要平衡。3.2 借助工具提升效率手工测试能确认漏洞但覆盖不了全站所有参数。这时候就得靠工具批量跑。我常用的几款Burp Suite Intruder抓到一个带路径参数的请求后把参数值设成 payload 位置加载一个路径穿越字典跑一遍就能看到哪些 payload 返回了非正常响应。Burp 的响应对比功能很好用能自动标记出长度或者状态码不同的响应。dirsearch虽然主要用于目录扫描但它的字典里包含一些常见穿越路径也可以扫/download/这类接口发现隐藏文件。ffuf灵活度最高可以把file参数设为变量配合-w指定字典开多线程快速跑。命令大概是ffuf -u http://target.com/download.php?fileFUZZ -w lfi.txt -mc 200 -fs 12345-mc 200只看 200 响应-fs 12345过滤掉正常响应长度减少噪音。字典方面比较经典的是SecLists里的LFI-Jhaddix.txt覆盖了../、编码形式、绝对路径、Windows 风格等找目录遍历够用。工具能用是一回事别忘了一条铁律只在授权目标上扫描。未经授权跑扫描器不管出于什么目的都可能给自己惹麻烦。练习环境推荐 Pikachu、DVWA 这类自带靶场的平台CTFHub 上也有一整个目录遍历专题可以随便练手。3.3 自动化扫描与 AI 挖洞的一点看法最近“AI 自动化挖漏洞”的话题很热但是对目录遍历这类漏洞AI 和自动化脚本更多的价值在于“发现可疑点”而不是“直接确认漏洞”。市面上很多扫描器会报出一堆“疑似路径穿越”点进去一看全是误报——可能的场景包括参数值里确实带../但那是合法的资源路径或者响应里包含root:字样但其实只是在渲染一个示例文本。真正的确认环节还得靠人来看响应内容、看应用上下文。所以我的态度是可以用自动化工具做全站摸排但确认漏洞和评估影响永远要人工复核。AI 生成一个“漏洞报告”很容易但报告里的复现步骤和影响分析是否真实可落地才是决定它有没有价值的关键。对新手来说与其迷信“AI 挖漏洞赚钱”不如先把目录遍历这种经典漏洞的原理和判断方法搞清楚——这恰恰是 AI 做不好的部分。4. 修复与加固从代码到基础设施4.1 代码层白名单与规范化修复目录遍历正确的姿势是“白名单校验 路径规范化检查”而不是“过滤黑名单”。先说最省心的方案如果业务场景是下载文件而且文件名就那么固定几种直接用白名单。比如?php $allowed [report_2024.pdf, report_2025.pdf]; if (!in_array($_GET[file], $allowed)) { http_response_code(403); exit; } readfile(/var/www/downloads/ . $_GET[file]); ?白名单意味着攻击者只能选你允许的东西路径穿越无从谈起。但这种方案局限性大很多业务确实需要动态传路径。那就得规范化校验。PHP 的正确做法是realpath()之后再比对前缀?php $base /var/www/downloads/; $input $_GET[file]; $fullPath realpath($base . $input); if ($fullPath false || strpos($fullPath, $base) ! 0) { http_response_code(403); exit; } readfile($fullPath); ?realpath()的作用是把..、符号链接全部解析成最终的绝对路径。比如realpath(/var/www/downloads/../../../etc/passwd)返回/etc/passwd然后strpos()检查发现它不以/var/www/downloads/开头直接拒绝。注意realpath()对不存在的文件会返回false所以如果业务里文件可能不存在得先处理好这个分支。Java 里对应的方案是Path.normalize()和startsWith()Path base Paths.get(/var/www/downloads).toRealPath(); Path target base.resolve(request.getParameter(file)).normalize(); if (!target.startsWith(base)) { response.sendError(403); return; }Python 的处理逻辑类似from pathlib import Path base Path(/var/www/downloads).resolve() target (base / user_input).resolve() if base not in target.parents and target ! base: raise PermissionError这里有个容易被忽略的坑resolve()和realpath()都会解析符号链接。如果下载目录里有软链接指向系统目录校验也可能被绕过。排查时记得检查下载目录里有没有可疑的软链接。4.2 服务器与中间件加固代码修完了服务器层也不能裸奔尤其是文件上传目录这种高风险位置。第一件事上传目录禁用脚本执行权限。比如上传目录是/var/www/html/uploadsNginx 就指定这个目录不解析 PHPlocation /uploads/ { location ~ \.php$ { deny all; } }Apache 的话用Directory块配合php_admin_flag关闭执行权限或者用.htaccess写php_flag engine off。这样即使上传了脚本也执行不了。第二件事应用运行账户的最小化权限。大部分 Web 服务跑在www-data或nginx用户下系统关键文件如/etc/shadow、应用配置文件的读取权限要收掉。很多目录遍历漏洞能读到/etc/shadow不是因为穿越多厉害而是服务账户权限太大。第三件事Nginx 层面对请求 URI 做一些基础拦截。虽然不能根治但能挡掉一批无脑扫描if ($request_uri ~* \.\.) { return 403; }注意这种正则拦截只是缓解手段不能当成修复方案。攻击者通过编码绕过就能打穿正则它是防线之一不是最后一道。部署层面有条件就上容器或者 chroot 隔离把应用锁在固定目录里。即使某个应用被穿出 Web 根目录也穿不出容器。这个思路在微服务架构下尤其现实每个服务一个容器攻击面天然缩小。4.3 漏扫与 SDL 流程代码修复和服务器加固是一次性的“止血”但长期来看把目录遍历这类漏洞挡在发布之前才是治本。我建议在开发流程里加两道检查第一道是静态代码扫描。Semgrep、CodeQL 这类工具能直接扫出“用户输入拼接到文件路径”的可疑模式。比如 Semgrep 有一条规则专门匹配$_GET或request.getParameter直接进readfile、File、open这类调用。开发者在本地就能跑提交代码前扫一遍成本极低。第二道是动态测试。每次发版前用漏扫工具对测试环境跑一遍全站扫描重点关注文件下载、文件上传、图片预览这几个入口。Xray、AWVS、OpenVAS 都能做基础检测工具搜出来的“疑似路径穿越”再人工复核一轮基本能拦住。另外漏洞修复报告这件事值得多说两句。行业内很多漏洞报告写得非常敷衍“存在目录遍历漏洞建议修复”复现步骤缺失影响范围含糊开发根本没法下手。一份合格的修复报告至少要有四块内容漏洞描述与危害完整的复现步骤包括请求包和响应包影响范围哪个接口、哪些参数、可能读取哪些文件修复方案代码示例或配置修改。把报告写到这个程度开发和安全的沟通成本能降低一半——这是我真实项目里的体会不是套话。5. 常见问题与排查心得5.1 典型问题排查表把我在项目中遇到过的、以及同行问得最多的几个问题整理成一张表按图索骥排查会快很多问题现象可能原因排查思路过滤了../还是被绕过黑名单不完整编码/反斜杠/绝对路径被漏掉检查过滤逻辑是否覆盖编码形式改用白名单 realpath 方案Linux 能读Windows 读不了路径分隔符不同payload 用了/Windows 用..\..\..\windows\win.ini测试直接传/etc/passwd没反应应用拼接路径时强制加了前缀绝对路径被覆盖用../逐层跳或观察报错信息里拼出来的完整路径realpath()返回 false目标文件不存在或 PHP 权限不足先确认文件存在再确认运行账户对目标目录有访问权限响应一直是 403有 WAF 或中间件拦截了../换编码 payload 测试WAF 拦截不代表代码安全需看代码层修复扫出来一堆“疑似”人工复核全是误报工具字典过宽响应内容匹配不严谨人工看响应体确认是否真的包含目标文件内容和业务特征目录遍历还有一个非常常见的混淆点目录遍历Path Traversal和目录列表Directory Listing是两回事。目录列表是访问一个目录时服务器自动展示文件列表属于 Apache/Nginx 的 Indexes 配置问题不存在越权目录遍历是通过路径控制读取任意文件是访问控制缺陷。两者修复方式完全不同写报告时千万不要混用。5.2 容易被忽略的几个细节第一不要只盯着 GET 参数。目录遍历可以出现在 POST 参数、Cookie、甚至 HTTP 头的自定义字段里。我就碰过一次某个系统的语言切换功能把lang存在 Cookie 里后端拿这个值拼模板路径Cookie 里塞一个../../../../etc/passwd模板渲染报错就把文件内容带出来了。所以测试时要把请求的每一个位置都当路径入口来看。第二不同软件的解码次数决定了绕过方式。Apache 默认会对 URL 做一次解码PHP 再解一次如果应用开发时又手动解了一次码那%252e%252e%252f就变成了../。反过来如果中间只有一层解码双重编码的 payload 反而会被当成普通文件名。测试时要基于具体技术栈选择 payload不能一套模板打天下。第三测试目录遍历要在授权范围里做并且留意日志。路径穿越请求会非常显眼地出现在访问日志里一串../想藏都藏不住。某些业务系统还有入侵检测看到异常路径直接封 IP。我见过有人在生产环境下乱打 payload 导致被封机房 IP 的案例。靶场随便测生产环境先写好范围再动手。5.3 修复优先级与个人体会如果让我给一个修复优先级我会这么排立刻修代码层所有文件操作入口改成白名单或 realpath 校验这个是根治。尽快做服务器加固上传目录禁脚本执行应用账户收权限。短期防护WAF 加临时规则拦截../、编码穿越、绝对路径等特征。长期治理静态扫描接入 CI漏扫纳入发版流程。最后说一点个人体会。目录遍历这类漏洞之所以常年存在不是因为技术多复杂而是因为开发者太习惯“把用户输入当成字符串拼接进路径”这是编程时最自然的写法也是最危险的写法。我参与修复过好几个类似漏洞最深的感受是与其跟攻击者玩“谁过滤规则更全”的游戏不如直接改变代码逻辑从根上堵住路径拼接这条路径。哪怕只是把拼接路径改成“先拼接再 validate”效果也比加十条黑名单强得多。这个思路不仅适用于目录遍历也适用于文件上传、文件包含甚至 SQL 注入——安全问题的根源往往不是某个字符而是对输入的不信任。你要是想验证自己的系统有没有这个问题也甭整花活就拿一个../字符串意识一下自己代码里有没有“直接拼接”的场景答案基本就出来了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑