尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Java Web路径穿越漏洞实战:从WEB-INF泄露到安全防御

Java Web路径穿越漏洞实战:从WEB-INF泄露到安全防御
📅 发布时间:2026/7/1 5:48:38

1. 项目概述:一次典型的Java Web路径穿越漏洞实战复盘

最近在整理过去的CTF解题笔记,翻到了这道来自RoarCTF 2019的“Easy Java”。这道题在当年算是一个经典的Java Web路径穿越漏洞案例,它巧妙地利用了Java Web应用对文件下载功能的不安全实现,结合对WEB-INF目录结构的理解,来获取敏感的配置文件。很多刚接触Java Web安全的同学,可能对WEB-INF这个目录的“神圣不可侵犯”性有误解,觉得它被容器保护得很好,这道题就是一个很好的“祛魅”过程。它不涉及复杂的框架漏洞或反序列化,核心就是最基础的路径遍历(Path Traversal)和对Web应用标准目录结构的认知。通过复现这道题,我们能清晰地看到,一个看似简单的文件下载接口,如果缺乏有效的输入校验和路径控制,会带来多么严重的信息泄露风险。无论你是正在打CTF的新手,还是想巩固Web安全基础的开发者,这个案例都值得深入琢磨一下。

2. 漏洞原理与场景深度拆解

2.1 核心漏洞:不受控的文件路径参数

这道题目的场景非常明确:一个提供了文件下载功能的Java Web应用。通常,前端会有一个列表或链接,点击后触发类似/download?filename=xxx.pdf的请求,后端则根据这个filename参数去服务器特定目录(比如/files)读取文件并返回。

漏洞的根源就在于,这个filename参数完全由用户控制,并且后端程序在拼接文件路径时,没有进行任何规范化(Canonicalization)和校验。攻击者可以注入包含路径遍历序列(如../)的字符串。

例如,假设后端代码是这样写的(概念性代码):

String basePath = "/var/www/app/uploads/"; String filename = request.getParameter("filename"); File file = new File(basePath + filename); // ... 读取文件并输出给用户

如果用户传入filename=../../../etc/passwd,那么最终拼接的路径就变成了/var/www/app/uploads/../../../etc/passwd,经过系统路径解析后,就等价于/etc/passwd。这就实现了跨越应用目录,读取服务器上任意文件的目的。

注意:在Unix-like系统和Windows系统上,路径遍历的表示方法略有不同(../vs..\),但原理一致。Java的File类会处理这些序列。

2.2 关键目标:WEB-INF目录与web.xml

在标准的Java Web应用(遵循Servlet规范)中,WEB-INF是一个位于应用根目录下的特殊目录。它的特殊性在于:

  1. 客户端不可直接访问:Servlet容器(如Tomcat, Jetty)会阻止任何直接来自客户端的对WEB-INF和META-INF目录下资源的请求。你无法通过http://target.com/app/WEB-INF/web.xml直接访问到它。
  2. 存放核心配置与代码:WEB-INF目录下通常包含:
    • web.xml:Web应用部署描述文件,是核心配置文件,定义了Servlet、Filter、Listener等。
    • classes/:存放编译后的Java类文件(.class)。
    • lib/:存放应用依赖的JAR包。

正因为客户端无法直接访问,一些开发者会误以为其中的文件是绝对安全的。然而,如果存在上述的路径穿越漏洞,并且应用本身有权限读取这些文件,那么WEB-INF的“保护”就形同虚设了。

web.xml文件是本题的“Flag”所在。它里面可能包含数据库连接信息、敏感接口路径、甚至是后端的逻辑密码(在这道CTF题中,flag很可能就以注释或某个参数值的形式藏在里面)。通过路径穿越读取web.xml,是Java Web安全信息收集中非常经典的一步。

2.3 场景还原:题目可能的界面与交互

根据“Easy Java”这个名称和常见出题套路,我们可以推测题目环境可能提供了一个非常简单的Web界面。也许是一个“帮助”页面,里面提了一下有个文件下载功能;或者更直接,页面上就有一个输入框,写着“输入文件名下载”,旁边放个“Download”按钮。

初始尝试可能是下载一个已知的、正常的文件,比如help.pdf或readme.txt,以确认下载功能正常工作。然后,攻击的思路便转向:我能否利用这个功能,去读取下载目录之外的文件?特别是那个受保护的WEB-INF/web.xml文件。

这里就引出一个关键问题:我们知道目标文件是WEB-INF/web.xml,但我们从哪个起点开始穿越呢?我们需要知道文件下载功能设置的基准目录(basePath)在哪里。是应用根目录?还是某个子目录?这通常需要一些探测或猜测。

3. 解题步骤与实操过程详解

3.1 信息收集与功能探测

第一步永远是观察。访问目标网址,查看页面源码、JavaScript文件、以及可能的注释。题目可能在前端给出提示,比如注释里写着“下载功能仅供下载/doc目录下的文件”。

更重要的步骤是直接测试下载接口。通过浏览器开发者工具的Network面板,观察点击下载按钮时发出的请求。假设我们捕获到的请求是:

GET /download?filename=help.doc HTTP/1.1

这就确认了接口路径和参数名。

手动修改参数进行测试:

  1. 测试基础遍历:尝试filename=../../../。观察响应。如果是目录列表泄露,可能会返回错误信息或403/500状态码。如果服务器配置了默认索引页,可能返回200但内容是索引页。最理想的情况是返回一个包含WEB-INF的目录列表。
  2. 测试绝对路径:有些情况下,如果后端直接使用filename参数创建File对象,甚至可能支持绝对路径(如filename=/etc/passwd),但这道Java题大概率不支持。

3.2 构造路径穿越Payload

这是最核心的一步。我们需要找到从下载功能的基准目录到WEB-INF/web.xml的相对路径。

在Java Web应用中,一个Servlet的当前工作目录(ServletContext的根路径)通常是Web应用的根目录。如果下载Servlet没有特意设置basePath,而是直接使用filename,那么new File(filename)就会相对于应用根目录(或者说是Servlet容器的当前工作目录,但通常是应用根目录)来寻找文件。

一个经典的Payload是:filename=WEB-INF/web.xml如果下载Servlet的路径解析是相对于应用根目录的,那么这个请求就会直接尝试读取应用根目录下的WEB-INF/web.xml文件。

但更常见的情况是,下载功能被限制在某个子目录,比如/files或/downloads。这时,我们就需要向上回退。

假设基准目录是/var/www/tomcat/webapps/ctfapp/downloads/,而web.xml在/var/www/tomcat/webapps/ctfapp/WEB-INF/web.xml。 那么,从downloads目录回到应用根目录,需要../。再从根目录进入WEB-INF,所以完整的相对路径是:../WEB-INF/web.xml。

因此,Payload尝试顺序通常是:

  1. WEB-INF/web.xml(直接读取)
  2. ../WEB-INF/web.xml(回退一层)
  3. ../../WEB-INF/web.xml(回退两层)
  4. ../../../WEB-INF/web.xml(回退三层)

在实战或CTF中,可能需要多次尝试。你可以通过不断添加../来向上遍历,直到读到文件或返回错误。

3.3 利用漏洞读取web.xml

当我们构造出正确的Payload,比如filename=../../../WEB-INF/web.xml(假设需要回退三层),并向/download接口发起请求时,如果漏洞存在且路径正确,服务器就不会返回一个文件下载,而是直接将web.xml的内容输出到HTTP响应体中。

实操记录: 使用curl工具或浏览器直接访问构造的URL:

curl 'http://target-ctf-server:port/download?filename=../../../WEB-INF/web.xml'

或者使用Burp Suite的Repeater模块,手动修改并重放请求。

成功的响应特征:

  • 状态码:通常是200 OK。
  • 响应头:Content-Type可能是application/xml、text/xml或者甚至是application/octet-stream(如果后端没有正确设置MIME类型)。
  • 响应体:直接就是web.xml文件的XML格式内容。

这时,你需要仔细查看这个XML文件的内容。Flag可能以以下几种形式存在:

  1. 作为注释:<!-- flag{this_is_the_flag} -->
  2. 作为某个初始化参数的值:<param-value>flag{config_password_here}</param-value>
  3. 作为某个Servlet或Filter的名称:虽然不常见。
  4. 藏在文件末尾或某个不起眼的配置项里。

3.4 扩展利用:读取Class文件与源码泄露

拿到web.xml后,解题可能就结束了。但作为一次完整的学习,我们可以思考更深层次的利用。web.xml里定义了Servlet和其对应的处理类。例如:

<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.ctfapp.servlet.LoginServlet</servlet-class> </servlet>

我们知道了处理登录的类文件是com.ctfapp.servlet.LoginServlet。这个类文件编译后位于WEB-INF/classes/目录下,对应的路径是WEB-INF/classes/com/ctfapp/servlet/LoginServlet.class。

既然我们已经有了路径穿越漏洞,我们完全可以尝试去读取这个.class文件:filename=../../../WEB-INF/classes/com/ctfapp/servlet/LoginServlet.class

.class文件是字节码,我们可以使用反编译工具(如JD-GUI、CFR、FernFlower)将其还原成Java源代码。在真实的渗透测试或更复杂的CTF题中,这可能会泄露关键的业务逻辑、加密算法、硬编码的密钥等,为进一步攻击(如逻辑漏洞、反序列化)铺平道路。

4. 漏洞挖掘与防御的深层思考

4.1 为什么这种漏洞会发生?

从开发角度,原因无非以下几点:

  1. 安全意识不足:开发者认为文件名参数是前端可控的,或者只会在预设列表中选择,忽视了用户可直接修改HTTP请求。
  2. 对Java File API的误解:认为new File()只会在当前目录下操作,或者不了解路径遍历序列的威力。
  3. 缺乏输入校验:没有对用户输入的filename参数进行“白名单”校验(只允许特定文件名)或“规范化+校验”处理。
  4. 框架误用:可能使用了某些框架的便捷方法,但没有仔细阅读文档,不知道这些方法可能存在安全风险。

4.2 如何防御路径穿越漏洞?

防御的核心原则是:永远不要信任用户输入,对文件路径进行严格的白名单控制。

  1. 白名单校验:这是最有效的方法。如果下载功能只允许下载少数几个已知文件,那么直接维护一个允许的文件名列表(Map),将用户输入的参数与列表比对,只返回匹配的文件。

    Map<String, String> allowedFiles = new HashMap<>(); allowedFiles.put("guide", "/secure/path/guide.pdf"); allowedFiles.put("help", "/secure/path/help.doc"); String fileKey = request.getParameter("key"); // 不用filename了,用key String realPath = allowedFiles.get(fileKey); if (realPath == null) { // 返回错误,文件不存在 return; } File file = new File(realPath);
  2. 路径规范化与校验:如果必须支持一定动态性,则:

    • 规范化路径:使用File.getCanonicalPath()或Path.normalize().toAbsolutePath()来获取规范化的绝对路径。
    • 校验路径前缀:确保规范化后的路径,是以你允许的基准目录(BASE_DIR)开头的。
    String userInput = request.getParameter("filename"); // 定义允许的基准目录 File baseDir = new File("/var/www/app/safe_download_area"); // 构造用户请求的文件 File requestedFile = new File(baseDir, userInput); // 获取规范路径 String canonicalPath = requestedFile.getCanonicalPath(); // 检查规范路径是否以基准目录的规范路径开头 if (!canonicalPath.startsWith(baseDir.getCanonicalPath() + File.separator)) { // 路径穿越尝试!拒绝请求。 throw new IllegalArgumentException("Invalid file path."); } // 安全,可以读取文件
  3. 使用资源ID而非路径:在数据库中存储文件,前端通过文件ID(如UUID)来请求下载,后端根据ID从数据库或安全存储中获取文件流。

  4. Web服务器配置:在Nginx或Apache层面,可以配置规则阻止请求中包含../的URL。

4.3 CTF中的变体与进阶思考

在更复杂的题目中,出题人可能会设置一些障碍:

  • 过滤../:后端代码可能用replaceAll("\\.\\./", "")或replace("\\.\\./", "")来过滤../。但可能存在双写绕过(....//过滤一次后变成../)或使用URL编码(%2e%2e%2f)绕过。
  • 强制添加后缀:后端可能自动为输入添加.pdf后缀。这时需要利用%00(空字节截断)或路径参数(filename=../../../WEB-INF/web.xml%00),但在高版本JDK和Servlet容器中,空字节截断通常已失效。另一种思路是考虑目录遍历后,目标文件本身是否需要后缀。
  • 读取其他敏感文件:除了web.xml,还可以尝试读取WEB-INF/classes/下的.class文件、/etc/passwd、/proc/self/environ(Linux环境变量)、应用日志文件等,进行信息收集。

复现这道“Easy Java”题目,绝不仅仅是为了得到一个Flag。它像一把钥匙,打开了Java Web应用安全中“访问控制”和“输入校验”这两扇最基础也最重要的大门。理解了路径穿越,你就能举一反三,在遇到文件上传、文件包含、模板注入等其他漏洞时,拥有更敏锐的嗅觉。下次当你看到任何一个由用户输入控制的文件路径参数时,心里都应该立刻响起警报:这里,会不会是下一个“Easy Java”?

相关新闻

  • 别再手动敲代码了!用STM32CubeMX 6.10.0图形化配置你的第一个FreeRTOS工程(STM32F407探索者)
  • 杰理之支持提示音断点播放【篇】
  • 我把橘子洲头做成了AI客服:本地大模型落地的第一个真实场景

最新新闻

  • 避开‘倒π’现象:为什么实际通信系统更偏爱2DPSK而非2PSK?
  • TikTok 网红营销怎么做?从达人筛选到合作流程详细解析
  • 医学影像智能分析革命:FAE如何重塑放射组学研究范式
  • 施工图CAD看图软件怎么选?多款主流工具实测对比
  • 别再死记硬背Frenet标架了!用OpenCASCADE的GeomFill_Trihedron枚举,5分钟搞懂曲线曲面局部坐标系
  • Java内存马技术解析:MemShellParty框架原理与攻防实践

日新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号