ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Vulhub 实战:Spring Framework + Jetty 路径穿越漏洞 CVE-2025-41242 的 “Ghost Bits“ 解码不一致原理与复现

Vulhub 实战:Spring Framework + Jetty 路径穿越漏洞 CVE-2025-41242 的 “Ghost Bits“ 解码不一致原理与复现 Vulhub 实战Spring Framework Jetty 路径穿越漏洞 CVE-2025-41242 的 Ghost Bits 解码不一致原理与复现【免费下载链接】vulhubPre-Built Vulnerable Environments Based on Docker-Compose项目地址: https://gitcode.com/GitHub_Trending/vu/vulhubCVE-2025-41242 是由 Spring 框架与 Jetty 之间 URI 解码行为不一致所导致的路径穿越漏洞Spring 的安全检查看到的是一段无害的中文 Unicode 字符串而 Jetty 却将其解释为致命的../最终可读取/etc/passwd等任意文件。本文基于 Vulhub 的 spring/CVE-2025-41242 环境完整讲解该漏洞的底层 Ghost Bits 根因、版本影响范围与适用前提并给出可复制、可运行的两种原始请求复现方法Python socket 脚本与 Yakit 原始数据包。漏洞背景与受影响版本范围Spring Framework 是 Java 生态中最广泛使用的应用框架之一Jetty 则是 Spring Boot 应用中常见的内嵌 HTTP 服务器。当应用部署在内嵌 Jetty之上且使用spring-webmvc的静态资源/路径解析逻辑时CVE-2025-41242 才会被触发。根据上游安全公告 GHSA-r936-gwx5-v52fspring-webmvc的受影响版本范围为受影响版本区间修复版本5.3.0–5.3.435.3.446.0.0–6.0.296.0.306.1.0–6.1.216.1.226.2.0–6.2.96.2.10还有一个容易被忽略的关键前提把%u002e解码为%2e的URIUtil.encodePathSafeEncoding方法仅在jetty-util 12.0中存在而spring-boot-starter-parent从3.2.0起才默认引入 Jetty 12。因此即便 Spring 端版本落在上述受影响区间内更老的 Spring Boot 组合例如 3.1.x Jetty 11也无法触发这条漏洞链——漏洞需要Spring 端在范围内与Jetty 12 的%u解码行为两个条件同时成立。Vulhub 本环境选用的正是 Spring Boot 3.2.4 默认spring-boot-starter-jetty的组合恰好完整满足触发条件。根因剖析StringUtils.uriDecode 中的 Ghost Bits漏洞根源位于 Spring 的StringUtils.uriDecode。该方法在逐字符解码 URI 时遇到非%xy形式的字符会直接调用ByteArrayOutputStream.write(int)而该方法只保留传入 16 位 Javachar的低 8 位高 8 位被静默丢弃即 Ghost Bits。这导致某些中文字符在解码过程中被悄悄炼成特定 ASCII 字节中文字符Unicode低 8 位折叠后 ASCII阮U962E0x2E.严U4E250x25%灵U70750x75u丰U4E300x300甲U75320x322来U67650x65e于是攻击者构造的字符串阮严灵丰丰甲来会被静默伪造为 ASCII 字符串.%u002e。这个结果之所以能绕过全部防护是因为它同时躲开了 Spring 侧的三道检查Spring 的isInvalidPath检查的是字面量../而.%u002e中并不含字面的../Spring 的isInvalidEncodedPath走的是标准URLDecoder它不认识非标准的%uXXXX转义因此也不会把它还原成..表面上看它只是一串无害的中文/Unicode所有安全检查全部放行。随后路径被交给底层 Jetty 的PathResource#resolve而 Jetty 在URIUtil.encodePathSafeEncoding中却主动把%u002e当作 Unicode 转义解码为.使请求路径中的/.%u002e/在文件系统层面变成/../跨目录穿越就此完成。一句话概括Spring 看到的是无害中文Jetty 解释的却是致命的../。上游修复Spring 官方提交 24e66b63的思路也很直接将ByteArrayOutputStream.write替换为StringBuilder逐字符append字符以完整 16 位追加、不再发生高位截断从根源上消除 Ghost Bits 现象。环境架构与启动Vulhub 为这个环境提供了预构建镜像 vulhub/spring-boot-jetty:3.2.4编排文件极其简单services: web: image: vulhub/spring-boot-jetty:3.2.4 ports: - 8080:8080 - 5005:5005从源码侧看该镜像由 base/spring/spring-boot-jetty/3.2.4 目录构建pom.xml 以spring-boot-starter-parent:3.2.4为父 POM依赖spring-boot-starter-web并显式排除了spring-boot-starter-tomcat再引入spring-boot-starter-jetty——即把默认的内嵌 Tomcat 替换为 Jetty这正是漏洞触发所必需的部署形态Application.java 是一个最小的SpringBootApplication入口应用仅暴露 8080HTTP与 5005JDWP 调试端口访问根路径会看到一个简单的 Hello World 页面。执行以下命令启动环境docker compose up -d等待数秒待服务启动完成后访问http://your-ip:8080/确认看到 Hello World 页面即表示靶机就绪。该环境同时在 environments.toml 中注册path spring/CVE-2025-41242可通过 Vulhub 的自动化测试框架进行批量验证。触发条件详解为什么浏览器、curl、Burp 都复现不了复现本漏洞有两个硬性条件理解它们是看懂 PoC 的前提条件一阮严灵丰丰甲来必须以原始 UTF-8 字节直接发送。Ghost Bits 截断只在StringUtils.uriDecode处理的字符串中已经存在 16 位char值时才会发生如果中文字符在传输前被 percent-encoding 成%E9%98%AE这样的 ASCII 三元组解码器走的是另一条不含截断逻辑的代码路径漏洞链直接断裂。而浏览器、curl、Burp Suite 及绝大多数代理工具在发送前都会对 URL 路径做规范化——把高位 Unicode 字符自动编码为 ASCII从而破坏触发条件。因此必须使用能逐字节构造并发送原始 HTTP 请求的工具。条件二目标文件名中至少要有一个字符被 percent-encode。例如把passwd写成passw%64d的 ASCII 码为 0x64。否则 Spring 的路径匹配逻辑会提前短路根本不会调用到那段有缺陷的解码逻辑。复现方式一Python socket 脚本 poc.pyVulhub 在 spring/CVE-2025-41242/poc.py 提供了完整的 PoC 脚本它直接把阮严灵丰丰甲来的原始 UTF-8 字节GHOST_BITS_SEG 阮严灵丰丰甲来.encode(utf-8)写入 socket绕过一切客户端的 URL 规范化。阅读源码可以看到几个对应上述触发条件的关键实现build_request构造的请求路径为/ 7 段(阮严灵丰丰甲来/) 编码后的目标路径7 层..足以跨越任意应用根目录encode_target_path实现条件二去掉目标路径的前导/并对最后一个字符做 percent-encoding如passwd→passw%64同时校验末字符必须是 ASCIIsend函数在https目标下用ssl模块透明地包裹 socket因此前置 HTTPS 的 Jetty 服务同样可用。常用命令# 读取靶机环境中的 /etc/passwd默认目标文件 python3 poc.py http://your-ip:8080 # 通过 -f 指定其他文件 python3 poc.py http://your-ip:8080 -f /etc/hosts # HTTPS 自签证书加 -k 跳过 TLS 校验 python3 poc.py https://your-ip:8443 -f /etc/passwd -k脚本把响应头写到 stderr、文件正文写到 stdout因此可以直接用重定向保存抓取到的文件python3 poc.py http://your-ip:8080 -f /etc/passwd passwd.txt在 Vulhub 环境上运行即可看到/etc/passwd的完整内容被跨目录读出图中可见HTTP/1.1 200 OK与Content-Type: application/octet-stream正文逐行输出了容器内的 root、daemon、bin、sys 等账户条目证实任意文件读取成立。复现方式二Yakit 原始数据包如果你没有 Python 环境Yakit 的 HTTP Fuzzer 会原样发送请求行、不对高位字符做规范化同样可以满足原始 UTF-8 字节直送的条件。在请求面板中粘贴如下数据包并发送GET /阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/etc/passw%64 HTTP/1.1 Host: your-ip:8080 Connection: close左栏是原样包含中文路径的原始请求末段passw%64满足条件二右栏响应中同样返回了root:x:0:0:root:/root:/bin/bash等/etc/passwd内容与 poc.py 的结果一致。修复与防护建议结合本文的根因分析防护应从两端入手升级 Spring Framework 到修复版本5.3.44/6.0.30/6.1.22/6.2.10对应上游提交 24e66b63修复将ByteArrayOutputStream.write改为StringBuilder.append消除高位截断阮严灵丰丰甲来不再能被伪造为.%u002e核查内嵌服务器组合只有Spring 在受影响区间 Jetty 12的组合可触发使用 Jetty 11 及以下Spring Boot 3.1.x 及更早的部署不受此漏洞链影响但仍应同步升级 Spring 以修复底层解码缺陷纵深防御对静态资源服务与文件访问接口避免直接依赖框架内部的宽松路径匹配生产环境可考虑以反向代理层统一做严格的 URI 规范化与路径白名单校验压缩框架间解析差异带来的攻击面。需要强调的是本漏洞利用链高度依赖原始高位字节直送 至少一处 percent-encoding两个条件常规扫描器和标准 HTTP 客户端均无法自动构造出有效载荷这决定了它更多是定向攻击与渗透测试场景下的利用点但从防御角度看任何依赖框架路径解析做安全边界的代码都应警惕这类双组件解码语义不一致型缺陷。【免费下载链接】vulhubPre-Built Vulnerable Environments Based on Docker-Compose项目地址: https://gitcode.com/GitHub_Trending/vu/vulhub创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表