前言Java反序列化漏洞发展到今天单纯的原生无过滤反序列化漏洞已经极少。各类中间件、框架都引入类白名单机制把危险类拦截在反序列化入口。JEP-200就是Jenkins为了封堵Remoting通道反序列化攻击引入的防护多年来被视作Jenkins控制器最重要的安全边界之一。CVE-2026-70426SECURITY-3911的特殊之处不是JEP-200白名单本身失效而是代码异常分支里的漏校验。正常流程会走类过滤器校验一旦类加载抛出异常程序进入catch块直接使用父类resolveClass跳过全部安全校验。攻击者只要拿到Agent/Connect权限或者控制Agent进程就可以构造序列化载荷让控制器加载核心类路径内的恶意类在Jenkins主节点完成RCE。很多运维、安全人员会误判这个漏洞以为是公网直接打Jenkins页面的一键漏洞。真实攻击链路藏在CI/CD架构的控制器与Agent通信通道属于供应链攻击的典型入口。构建任务在Agent执行依赖包投毒、Agent账号被接管都能变成攻击控制器的跳板。控制器一旦失陷所有凭证仓库、代码仓库密钥、云账号、生产环境部署密钥全部暴露。本文从第一性原理拆解漏洞做对抗式审查从底层代码逻辑、攻击链路、环境复现、检测脚本、临时缓解、长期加固完整落地。附带Mermaid流程图与架构图可直接插入文章配图。所有检测脚本、验证代码可复制使用。1 漏洞基础信息CVE编号CVE-2026-70426Jenkins内部安全编号SECURITY-3911漏洞类型不安全反序列化绕过JEP-200类过滤器RCECVSS9.0 严重漏洞组件Jenkins Remoting通信库1.1 受影响版本Remoting库≤3384.v60d89463d9e0版本3355.3357.v931d3c992987除外Jenkins每周版≤2.575Jenkins LTS长期支持版≤2.568.1修复版本Jenkins 2.576Jenkins LTS 2.568.2Remoting ≥3386版本例外说明3355.3357.v931d3c992987版本已经修复该回退分支问题不受漏洞影响升级排查时不要误判。1.2 攻击前置条件对抗式审查重点这个漏洞不能无授权直接远程攻击必须满足下面任意一条攻击者控制Jenkins Agent代理进程Agent上可以执行自定义代码攻击者账号拥有Jenkins的Agent/Connect权限可以新建Agent节点建立Agent到控制器的Remoting通信通道。没有上述权限载荷无法投递到Remoting通信通道漏洞无法触发。很多厂商的漏洞通告简化描述容易让人误解成公网未授权RCE这是红队与蓝队排查时最容易踩的认知陷阱。漏洞载荷的限制只能使用Jenkins核心classpath内的类插件自带依赖类不能被反序列化。攻击者只能利用JDK原生类、Jenkins core内置类构造gadget链不能直接加载第三方插件的类。这个边界决定gadget选型范围也限制攻击载荷的构造思路。2 Jenkins Remoting与JEP-200底层原理2.1 Remoting通信架构Jenkins控制器Controller和Agent节点依靠Remoting库完成跨节点通信。Agent执行构建任务把执行状态、返回对象序列化后通过TCP通道传给控制器控制器下发任务对象到Agent。整个通道大量使用Java原生序列化传输对象。flowchart LR A[Jenkins Controller] --|Remoting TCP通道 Java序列化对象| B[Jenkins Agent] A -- C[JEP-200类过滤器br/反序列化前校验类白名单] C -- D[正常类加载流程] D -- E[业务逻辑执行]控制器在接收Agent发来的序列化对象时默认启用JEP-200类过滤器。过滤器维护白名单只有可信类才允许被反序列化阻断经典Java反序列化gadget。JEP-200在2018年正式落地把Jenkins Remoting从黑名单防护切换为白名单防护。在这之前Jenkins多次出现Remoting通道反序列化RCE。JEP-200上线后安全业界普遍认为Agent到控制器的序列化攻击面基本关闭。2.2 正常resolveClass流程Remoting自定义ObjectInputStreamEx继承原生ObjectInputStream重写resolveClass方法。读取序列化流中的类名称在try代码块内尝试使用自定义类加载器加载目标类加载成功调用filter.check()JEP-200过滤器校验类是否在白名单校验通过返回Class对象完成反序列化校验失败抛出异常终止反序列化。2.3 漏洞异常catch分支的回退路径无过滤漏洞根源在两处代码ObjectInputStreamEx.resolveClass和MultiClassLoaderSerializer$Input.resolveClass。try块内加载类发生ClassNotFoundException代码捕获异常直接调用super.resolveClass。super.resolveClass是父类ObjectInputStream原生方法完全没有执行JEP-200 filter校验。flowchart TD S[进入resolveClass] T[try 自定义类加载器加载类] F{加载成功?} OK[执行JEP-200 filter.checkbr/白名单校验] ENDOK[返回Class继续反序列化] FAIL[捕获ClassNotFoundException] BYPASS[调用super.resolveClassbr/无JEP-200校验] ENDBYPASS[返回Class继续反序列化] S -- T T -- F F --成功-- OK -- ENDOK F --失败-- FAIL -- BYPASS -- ENDBYPASS攻击者的目标就是主动制造ClassNotFoundException迫使代码进入这个未校验的回退分支。实现思路构造序列化数据让第一次类加载失败。例如传入空类加载器、伪造类加载标记触发异常走fallback路径。此时控制器使用控制器自身类加载器加载目标类不再经过JEP-200白名单。第一性原理视角安全校验只写在主逻辑异常分支没有做同等校验。安全编码里最常见的缺陷防护逻辑没有覆盖全部代码路径。对抗审查的核心思路不要只看正常业务路径遍历所有异常捕获分支。3 攻击完整链路拆解攻击者拿到Agent执行权限或者Agent/Connect权限创建受控Agent节点建立Agent到Controller的Remoting TCP通信通道构造恶意序列化对象精心设置类加载参数让第一次自定义类加载抛出ClassNotFoundException序列化数据通过Remoting通道发送给Jenkins控制器控制器调用ObjectInputStreamEx.resolveClasstry加载失败进入catch执行super.resolveClass跳过JEP-200校验加载Jenkins core classpath内的gadget类反序列化执行readObject方法触发gadget链在控制器主机执行系统命令执行结果沿Remoting通道回传给攻击者。攻击链路不是Web页面注入是跨节点通信通道的序列化攻击。这个攻击面在很多企业被低估。很多企业防火墙严格限制公网访问Jenkins 8080端口但内网Agent节点与控制器50000端口互通。一旦Agent被入侵就可以横向移动拿下控制器。供应链场景下开发人员提交的构建脚本、npm/maven依赖投毒都能在Agent执行阶段触发载荷发起对控制器的攻击。攻击者不需要拿到Jenkins账号只需要污染构建依赖在构建执行阶段触发攻击。4 源码缺陷定位与补丁分析4.1 漏洞原始代码片段Override protected Class? resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { try { Class? c classLoader.loadClass(desc.getName()); filter.check(desc, c); return c; } catch (ClassNotFoundException e) { return super.resolveClass(desc); } }try块内加载成功执行filter.check做安全校验。捕获ClassNotFoundException后直接返回父类resolveClass结果没有filter校验。MultiClassLoaderSerializer$Input.resolveClass存在完全相同的缺陷。4.2 官方补丁改动补丁在catch分支同样增加filter校验逻辑。不管是try内加载成功还是fallback到super.resolveClass拿到Class对象都必须执行filter.check()。Override protected Class? resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { Class? c; try { c classLoader.loadClass(desc.getName()); } catch (ClassNotFoundException e) { c super.resolveClass(desc); } filter.check(desc, c); return c; }把filter校验移出try块放到所有分支的公共出口。无论走哪条路径类对象返回前都必须经过JEP-200过滤器。这个改动消除了回退分支的安全缺口。同时Jenkins新增回归测试用例专门模拟ClassNotFoundException场景覆盖fallback路径防止后续版本迭代再次引入同类漏洞。5 本地复现环境搭建警告仅授权内网测试环境复现禁止对公网未授权Jenkins实例进行测试遵守网络安全法规。5.1 环境清单Jenkins版本2.575受影响版本Remoting版本3384.v60d89463d9e0JDK版本JDK11Jenkins推荐运行环境AgentJNLP Agent和控制器建立Remoting通道操作系统Linux Ubuntu 22.045.2 部署步骤下载jenkins.war 2.575版本启动控制器java -jar jenkins.war --httpPort8080初始化Jenkins新建Agent节点获取JNLP连接密钥启动Agent连接控制器50000 JNLP端口在Agent端部署测试代码构造序列化载荷通过Remoting Channel发送至控制器。5.3 复现关键点必须保证Agent和控制器Remoting通道正常连通载荷必须触发ClassNotFoundException进入fallback分支gadget类必须存在于Jenkins core classpath插件类无法使用反序列化成功后命令执行在控制器进程不是Agent。6 检测脚本资产扫描与漏洞检测下面两个脚本第一个Python脚本批量扫描内网Jenkins资产获取版本判断是否在受影响范围第二个Java简易检测代码用于验证Remoting通道通信。6.1 Python批量资产检测脚本可直接复制#!/usr/bin/env python3 # CVE-2026-70426 Jenkins 版本检测脚本 # 仅检测版本不做漏洞利用蓝队资产巡检使用 import requests import argparse import sys from concurrent.futures import ThreadPoolExecutor headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 } def check_jenkins(target): try: url f{target}/api/json resp requests.get(url, headersheaders, timeout5, verifyFalse) if resp.status_code 200: data resp.json() ver data.get(version, ) if not ver: print(f[INFO] {target} 识别为Jenkins未获取版本号) return # 判断版本范围 vulnerable False try: # 每周版 2.575 ; LTS 2.568.1 if ver.startswith(2.): if . not in ver: pass parts ver.split(.) major int(parts[1]) if len(parts)2: # 每周版 if major 575: vulnerableTrue elif len(parts)3: # LTS 2.568.1 if major 568 and int(parts[2]) 1: vulnerableTrue except Exception: pass if vulnerable: print(f[VULN] {target} | Version:{ver} 存在CVE-2026-70426风险) else: print(f[SAFE] {target} | Version:{ver} 版本不受影响) except Exception as e: print(f[ERR] {target} 连接失败 {str(e)[:60]}) def main(): parser argparse.ArgumentParser() parser.add_argument(-f, --file, help目标列表文件每行一个http://ip:port) parser.add_argument(-t, --target, help单个目标 http://ip:8080) parser.add_argument(-w, --workers, default8, typeint) args parser.parse_args() targets [] if args.file: with open(args.file,r,encodingutf-8) as f: for line in f: line line.strip() if line: targets.append(line) if args.target: targets.append(args.target) if not targets: print(请输入 -t 单个目标或者 -f 目标文件) sys.exit(1) with ThreadPoolExecutor(max_workersargs.workers) as executor: executor.map(check_jenkins, targets) if __name__ __main__: main()使用示例# 单个目标 python3 jenkins_cve202670426_scan.py -t [http://127.0.0.1:8080](http://127.0.0.1:8080) # 批量扫描 python3 jenkins_cve202670426_scan.py -f targets.txt说明脚本只做版本指纹识别不能证明可利用。版本匹配仅代表存在漏洞条件是否能利用还需要Agent/Connect权限或受控Agent。蓝队风险评估不能只靠版本判定必须结合权限模型、Agent接入管控综合判断。6.2 临时缓解措施无法升级时如果业务不能立刻升级Jenkins版本官方提供临时缓解方案在Jenkins启动参数添加系统属性收紧Remoting类过滤。-Dhudson.remoting.ClassFilter!*这个配置默认禁止所有类跨Remoting通道反序列化业务会受影响需要按需添加业务白名单类。仅作为临时应急长期方案还是升级。7 红队攻击面拓展与对抗审查很多安全人员只盯着Web页面忽略Agent侧的攻击入口。我们做对抗审查梳理真实场景的攻击入口。7.1 攻击入口1新增Agent节点Agent/Connect权限拥有Agent/Connect权限的用户可以新建JNLP Agent拿到Agent连接密钥在攻击者服务器启动Agent接入控制器。建立Remoting通道后发送序列化载荷。很多企业RBAC配置粗放普通开发账号被分配Agent/Connect权限这是高危配置。7.2 攻击入口2现有Agent被入侵Agent服务器被入侵漏洞、弱口令、恶意构建脚本攻击者在Agent进程内执行代码通过已存在Remoting通道发送载荷攻击控制器。这是供应链攻击最典型路径。构建任务执行npm、maven、pip依赖恶意依赖包在构建阶段执行代码接管Agent。7.3 攻击入口3云原生动态AgentK8s AgentKubernetes插件动态创建Agent Pod。如果构建脚本可以控制Pod攻击者拿到Agent Pod权限横向攻击控制器。云原生CI/CD场景下这个攻击面经常被忽略。7.4 对抗审查要点清单核查用户权限哪些账号拥有Agent/Connect权限审计Agent节点接入方式是否允许外部自建Agent审计Agent服务器基线Agent是否最小权限运行审计构建流水线是否允许不受控依赖包下载网络层面控制器50000端口是否过大范围开放访问日志审计Remoting通道通信日志、类加载异常日志。8 生产环境加固方案8.1 优先操作版本升级升级Jenkins到安全版本普通版升级 ≥2.576LTS长期支持版升级 ≥2.568.2升级同时Remoting库同步更新至≥3386。升级前备份JENKINS_HOME目录保存配置与凭证。8.2 权限最小化严格限制Agent/Connect权限只给运维管理员普通开发账号移除该权限Agent运行账号最小权限Agent进程不能拥有服务器root权限禁止匿名Agent接入关闭废弃的Agent协议。8.3 网络隔离Jenkins控制器50000 JNLP端口仅允许可信Agent网段访问控制器和Agent之间网络分段Agent所在网段不能访问控制器管理后台公网禁止直接暴露Agent JNLP端口。8.4 日志与监控开启Remoting通信日志监控异常类加载、大量ClassNotFoundException报错。告警规则短时间内大量Remoting通道抛出ClassNotFoundException大概率是漏洞探测或攻击尝试。8.5 流水线安全加固依赖包镜像扫描构建前做恶意依赖检测流水线脚本使用SCM审批不允许用户自由提交未审核脚本构建容器隔离Agent使用一次性容器构建结束销毁防止持久化入侵。9 漏洞与同类Jenkins漏洞横向对比CVE-2024-43044同样依赖Agent/Connect权限漏洞是文件读取。CVE-2026-70426是RCE危害等级更高。两者攻击入口一致都是Remoting通道。这一组漏洞证明Agent接入权限本质等同于控制器高风险权限。很多企业认知错误认为Agent只是执行构建Agent被攻陷不会影响控制器。CVE-2026-70426直接推翻这个认知边界。JEP-200作为防护机制设计思路没问题但安全校验只覆盖主流程异常分支遗漏。这是安全编码经典错误。安全校验不能假设程序永远走正常分支所有异常捕获、降级、回退逻辑都要复用相同安全校验逻辑。10 漏洞未来演进预判同类漏洞还会持续出现在序列化、类加载相关代码。开发人员处理异常时容易省略安全校验。这类缺陷静态代码扫描很难发现需要人工做路径遍历审计。CI/CD平台持续成为攻击热点。攻击者越来越喜欢利用构建流水线作为跳板供应链攻击会持续增加。Agent与控制器的信任边界是CI/CD安全最重要的防线。很多企业只加固Web页面忽略这个内部通信通道。Jenkins后续安全开发会强化全路径安全校验、增加更多回归测试覆盖异常分支。但只要Java序列化继续用于跨节点通信类似风险就不会完全消失。长期安全方案逐步减少跨节点Java序列化传输替换为JSON等非序列化通信协议。结尾互动你的企业Jenkins是否开放了Agent/Connect权限给普通开发账号你们在CI/CD安全建设里有没有把Agent当成攻击面重点管控