
导语企业常把 Jenkins 当成“运行构建任务的服务器”。从攻击者视角看它却更像软件交付体系的控制平面它知道代码从哪里来掌握构建和发布规则持有代码仓库、制品库、云平台与生产环境的凭据还能调度大量 Agent 执行任务。这意味着同一个远程代码执行漏洞出现在普通业务容器和 Jenkins Controller 上后果可能完全不同。2026 年 9 月 2 日Jenkins 发布安全公告披露 CVE-2026-84645。漏洞允许本应独立保存在顶层配置文件中的PersistenceRoot对象作为用户提交config.xml的嵌套字段被反序列化。这些对象随后可能进入 Stapler 的反射路由体系。官方确认经过精心组合攻击者能够触达保护不当的 Script Console最终造成远程代码执行。从代码上看这是一个对象图约束缺失问题从架构上看它是一条由配置数据通往 CI 控制平面最高权限能力的路径。真正需要回答的也不只是“Jenkins 是否已经升级”Controller 是否保存了生产凭据攻击者能否修改 Job、Agent 或插件已发布制品还能否被信任Git Tag、构建日志和制品签名是否足以证明交付链没有被篡改如果控制平面曾经失陷团队该从哪里重建信任本文将从官方补丁出发先拆解对象图漏洞再把风险一直追踪到源码、构建、签名、制品和部署环节。一、事实、推断与建议已确认事实漏洞编号为CVE-2026-84645Jenkins 内部编号为SECURITY-3972。Jenkins 官方将严重度标记为High。受影响版本为 Jenkins2.579及更早版本、LTS2.568.2及更早版本。修复版本为 Jenkins2.580和 LTS2.568.3均于2026 年 9 月 2 日发布。Jenkins 使用 XStream 序列化和反序列化配置与构建数据并用 JEP-200 类型过滤限制可反序列化类型。漏洞版本允许实现PersistenceRoot的对象作为用户提交 XML 的嵌套字段出现。这类对象随后可能通过 Stapler 处理 HTTP 请求。官方公告确认精心组合对象可以访问保护不当的 Script Console导致 RCE。官方修复提交0d731367在通用转换器中加入PersistenceRoot嵌套约束、单例保护与回归测试。工程推断如果 Jenkins Controller 保存代码仓库、制品库、云账户或生产环境凭据Controller 级代码执行可能威胁下游交付链。如果攻击者修改流水线后又恢复 Jenkins 内部配置单靠当前 Job 定义或应用日志可能无法证明历史制品可信。具体爆炸半径取决于 Controller 运行身份、网络、挂载、凭据范围、Agent 模型以及发布审批机制。本文建议升级和历史排查应同时进行。一旦发现 Controller 级可疑执行应将近期制品和部署置于待验证状态而不是只重装 Jenkins。恢复工作应以重新建立源码—构建—制品—部署的证据链为目标。未知事项截至核验时间本文引用的一手来源未确认该漏洞存在在野利用。官方没有公开完整利用 XML也没有披露具体攻击活动或失陷指标。本文不会补齐可武器化对象组合、真实端点或 Script Console 调用方式。二、为什么 Jenkins 是“控制平面”而非普通应用1. 它连接软件交付的多个信任域典型 Jenkins 环境会连接源码仓库 ↓ Jenkins Controller ↓ 构建 Agent ↓ 依赖与制品仓库 ↓ 签名服务 ↓ 测试 / 预生产 / 生产环境Controller 不一定亲自执行所有构建但它决定运行哪个 Job使用哪份 Jenkinsfile 或共享库将任务派发给哪个 Agent注入哪些 Credentials发布哪些制品触发哪个环境部署。2. 正常功能本身就是高风险能力Jenkins 无需隐藏的命令执行 Gadget 才能产生危害。运行脚本、调用 Agent、读取凭据和发布制品本就是其设计能力。安全边界的关键不是消灭这些能力而是确保只有正确主体、通过正确入口、在正确审计条件下使用它们。3. Controller 的输出会被组织信任来自 CI 的制品通常拥有更高可信度出现在官方制品仓库带有正式版本号可能拥有组织签名通过自动化部署进入生产日志显示“构建成功”。一旦控制平面失陷这些信任标志可能被攻击者借用。攻击者无需绕过每台生产服务器只需让交付系统替自己发布。三、漏洞链的第一步可信类型进入了错误位置JEP-200 已经限制类型Jenkins 在 2018 年引入 JEP-200反序列化策略从危险类黑名单转向允许规则默认只接受 Jenkins Core、插件或显式允许的类。这能回答这个 Java 类型是否来自 Jenkins 信任的代码但 CVE-2026-84645 需要回答另一个问题这个类型是否应该在当前文档的当前位置被重新构造PersistenceRoot 的语义按照 Jenkins 官方说明某些对象拥有自己的独立配置或构建文件通常是config.xml或build.xml。这些类型实现PersistenceRoot。它们不是普通值对象而是具有独立身份与生命周期的持久化根。概念上可以这样区分普通值颜色、阈值、名称、简单配置 对象引用指向已有 Job、Build、User 的标识符 持久化根拥有独立文件、身份和生命周期的完整对象漏洞版本的问题漏洞版本允许外部 XML 把一个PersistenceRoot类型作为普通字段嵌套进去configuration child classTrustedPersistenceRoot !-- 外部输入控制的嵌套对象状态 -- /child /configuration类本身可以在白名单里位置却违反了系统设计。因此核心公式是可信类型 非法位置 攻击者状态 不可信对象图四、第二步对象图与 Stapler 路由发生连接反序列化不只产生数据在简单 DTO 系统中反序列化结果可能只是被读取几个字段。Jenkins 对象更复杂。它们可能实现 Web 相关方法挂入父子对象关系注册或暴露动作参与队列与运行生命周期持有对 Jenkins 根对象的引用被 Stapler 作为请求路由节点访问。Stapler 的作用Jenkins 官方公告说明Stapler 根据命名约定通过反射访问与 HTTP 请求处理相关的代码元素。Jenkins 已对可路由类型和成员设置限制但对象图污染让攻击者获得了原本不应存在的路径节点。风险链由此从数据层跨入行为层提交 XML ↓ XStream 构造嵌套对象图 ↓ 非法 PersistenceRoot 出现在普通字段 ↓ 对象参与 Stapler 路由 ↓ 敏感 Web 能力变得可达安全审计不能停在“有没有危险构造器”传统反序列化审计常关注构造器、readObject()、readResolve()是否立即执行危险行为。本案提醒我们还必须追踪反序列化后的对象会去哪里是否会被路由框架遍历是否进入依赖注入容器是否被任务调度器消费是否暴露插件扩展点是否继承某个高权限上下文。危险行为可以发生在后续请求中而不是发生在解析 XML 的同一调用栈里。五、第三步Script Console 将 Web 可达性放大为控制平面执行Jenkins 官方文档明确指出Script Console 是运行在 Controller 或 Agent 运行时中的 Groovy Shell。它可以创建子进程并执行系统命令读取 Jenkins 进程可访问的文件解密 Jenkins 中配置的凭据修改系统配置和安全设置管理用户、插件、节点与 Job在 Agent 上执行脚本。官方文档甚至提醒获得 Script Console 访问能力本质上接近获得 Jenkins 管理员权限。因此CVE-2026-84645 的最终影响不是“看到了一个后台页面”而是进入 CI 控制平面的最高能力区域。需要明确的边界【事实】官方公告确认完整对象组合可能触达保护不当的 Script Console 并造成 RCE。【未知】官方没有公开利用所需的完整 XML、具体提交入口和现实攻击样本。【推断】攻击者获得 Jenkins 进程内执行后可能利用 Controller 已有权限影响代码、凭据、Agent 和发布流程是否能够做到必须根据实际部署验证。六、官方补丁保护的不是类名而是对象图结构官方修复提交为0d731367e08656f8cd1e8275f0e820f97af07fc61. 在通用转换器中拒绝非法嵌套核心检查加入RobustReflectionConverterif (PersistenceRoot.class.isAssignableFrom(type) !isSafePersistenceRootReference(reader)) { throw new CriticalXStreamException(...); }补丁将原本隐含的设计原则变成强制规则PersistenceRoot是文档根不能作为普通嵌套字段重新构造。2. 为什么修复不能“一刀切”Jenkins 的合法对象图中确实可能引用 Job、Build、User 或 Jenkins 根对象。如果直接禁止所有PersistenceRoot字段会破坏正常持久化和插件兼容性。补丁因此保留三类可证明安全的引用。回引用使用 XStream 的reference指向当前上下文中已经构造的祖先对象不创建新根对象也不注入新的嵌套状态。替换占位符使用resolves-to指向非PersistenceRoot的占位对象由readResolve()根据 ID 查找已有实例。同时要求不能通过class把占位符覆盖成完整根对象。单值引用目标类型注册SingleValueConverterXML 只携带文本标识符不包含子对象图再由转换器解析真实对象。3. 不能证明安全就拒绝补丁采用 fail-closed未命中安全模式拒绝类名无法解析拒绝class指向的类型没有单值转换器拒绝resolves-to仍然是PersistenceRoot拒绝。4. 阻止第二个 Jenkins 单例补丁还在 Jenkins 根对象的readResolve()中验证唯一性如果系统已经存在 Jenkins 单例又试图反序列化出不同实例则抛出异常。这相当于再加一道领域层防线即便通用对象图过滤遗漏单例自身也拒绝被复制。5. 安全异常必须中止加载补丁引入CriticalXStreamException让安全策略违规与普通兼容性错误分离。这是重要设计旧字段缺失或插件升级差异可以被容错但对象图安全不变量被破坏时不能跳过一个字段后继续使用半可信对象。七、无害模型同一种类型三个不同安全结论下面的 Python 代码只模拟对象图策略不读取 Jenkins XML、不连接 Jenkins也不执行任何命令。from dataclasses import dataclass from enum import Enum, auto class Position(Enum): DOCUMENT_ROOT auto() NESTED_NEW_OBJECT auto() SAFE_REFERENCE auto() class PersistenceRoot: pass dataclass class BuildJob(PersistenceRoot): object_id: str dataclass class PlainSetting: value: str ALLOWED_TYPES {BuildJob, PlainSetting} def vulnerable_policy(value_type: type) - bool: 缺陷模型只验证类型来源。 return value_type in ALLOWED_TYPES def fixed_policy(value_type: type, position: Position) - bool: 修复模型同时验证类型和对象图位置。 if value_type not in ALLOWED_TYPES: return False if issubclass(value_type, PersistenceRoot): return position in { Position.DOCUMENT_ROOT, Position.SAFE_REFERENCE, } return True安全测试def test_trusted_root_is_not_allowed_as_fresh_nested_object(): assert vulnerable_policy(BuildJob) assert not fixed_policy( BuildJob, Position.NESTED_NEW_OBJECT, ) def test_root_is_allowed_at_document_boundary(): assert fixed_policy( BuildJob, Position.DOCUMENT_ROOT, ) def test_existing_root_can_be_referenced_safely(): assert fixed_policy( BuildJob, Position.SAFE_REFERENCE, ) def test_plain_value_can_be_nested(): assert fixed_policy( PlainSetting, Position.NESTED_NEW_OBJECT, )模型说明类型可信 位于文档根 → 允许 类型可信 安全引用已有对象 → 允许 类型可信 新建为嵌套根对象 → 拒绝 普通可信值对象 嵌套 → 允许它不会复现真实漏洞却能帮助团队把“类型允许”和“位置允许”拆成两个独立测试维度。八、从 Controller RCE 到供应链影响五层风险模型第一层Jenkins 运行时攻击者可能读取或修改 Controller 进程能够访问的文件、配置与运行状态。应检查异常进程新文件和启动项插件目录变化用户与授权策略变化Script Console 访问未知 Agent 和节点配置。第二层凭据平面Jenkins 常保存 SCM、制品仓库、云、SSH、Webhook 和 Kubernetes 凭据。【推断】如果攻击者获得 Script Console 能力这些凭据可能受到威胁。轮换范围应由 Jenkins Credentials、环境变量、挂载文件和外部秘密管理系统共同确定。第三层构建平面攻击者可能修改 Job 定义替换共享库引用改变构建参数将任务派发到异常 Agent修改编译器、依赖源或构建脚本在打包前后插入额外步骤。即使源码仓库完全干净构建结果仍可能被污染。第四层制品平面如果 Jenkins 拥有上传和签名权限攻击者可能让恶意制品获得正式文件名和版本号官方制品仓库位置合法发布账号组织签名或构建证明自动化发布日志。因此“签名有效”只能证明签名密钥参与过不能在控制平面失陷后自动证明制品内容可信。第五层部署平面Jenkins 还可能直接操作 Kubernetes、云平台、虚拟机或发布系统。【推断】如果部署凭据没有独立审批和最小权限攻击者可能绕过制品仓库直接修改生产工作负载。这五层说明修复 Controller 漏洞只是止血恢复供应链信任还需要验证所有下游输出。九、如果 Jenkins 可能失陷哪些证据还能相信不应单独作为结论的证据Jenkins 页面显示构建成功Job 当前配置看起来正常Controller 内部日志没有异常制品具有合法版本号制品由 Jenkins 使用有效密钥签名Git 仓库没有可疑提交。这些证据都可能是真实的但在 Controller 失陷假设下不够独立。更有价值的外部证据Git 托管平台的独立审计日志受保护分支与签名提交记录制品仓库的不可变上传日志外部透明日志中的签名和来源证明云平台与 Kubernetes Audit Log身份代理、反向代理和 WAF 日志与 Jenkins 隔离的 EDR、DNS 和网络流量从干净环境执行的可复现重建结果。核心原则事件调查中同一信任域产生的多份日志不一定是多份独立证据。如果 Jenkins 能同时修改 Job、日志和制品那么三者仍属于一个可能被控制的证据域。需要用 Jenkins 无法改写的外部记录进行交叉验证。十、立即处置版本升级与暴露确认受影响版本Jenkins Weekly2.579 及更早版本 Jenkins LTS 2.568.2 及更早版本修复版本Jenkins Weekly2.580 Jenkins LTS 2.568.3P0 升级动作Weekly 用户升级到2.580或更高受支持版本。LTS 用户升级到2.568.3或更高受支持 LTS。使用实际 Controller 页面、进程或制品信息核验版本。对容器部署核验镜像 Digest 和 Pod 创建时间不只看可变 Tag。同步阅读 9 月 2 日公告中的插件漏洞完成插件更新评估。P0 临时收缩将 Controller 管理入口限制在可信网络与身份代理后。收紧配置、创建、管理和 Script Console 相关权限。阻断绕过反向代理直接访问 Controller 的路径。暂停不必要的高权限自动发布任务。确认 Controller 不以 root 或系统管理员身份运行。关于利用状态截至 2026 年 9 月 5 日本文核验的一手来源没有确认该漏洞存在在野利用。这意味着不应把“存在 RCE”写成“正在大规模攻击”但也不能因为没有公开攻击报告而推迟修复。CI 控制平面高权限、长生命周期和凭据密集的特征足以支持高优先级处置。十一、威胁狩猎从 XML 一直查到生产阶段一Controller 入口检查异常config.xml提交和配置更新不符合日常管理时间的配置操作新建或修改的用户、Token 与权限Script Console 页面访问来自未知来源的 CLI 或 API 操作配置加载时的 XStream 与安全异常。阶段二运行时行为检查Controller 上异常子进程非预期网络连接和 DNS 查询插件、脚本、初始化目录和临时目录变化新增 Agent、Agent Secret 访问和节点标签变化安全配置、日志设置或审计插件被关闭。阶段三流水线变化检查Jenkinsfile 来源是否变化共享库版本是否从固定提交改为浮动分支构建参数、依赖源和仓库地址是否变化是否出现未审批的 Shell、Groovy 或凭据绑定步骤构建是否被派发到异常 Agent发布步骤是否绕过原有审批。阶段四制品与签名检查同一版本是否存在不同摘要制品上传者、时间和来源 IP签名服务调用是否与批准的构建对应SBOM 与实际制品内容是否一致构建证明中的源码提交、构建器身份和参数是否完整是否存在构建后被替换的附件或镜像标签。阶段五部署环境检查云、Kubernetes 和发布平台审计日志非正常时间或主体执行的部署工作负载镜像 Digest 与批准记录是否一致Secret、ServiceAccount、RoleBinding 和网络策略变化生产环境中没有对应可信构建记录的二进制。十二、恢复信任不要只做“重装 Jenkins”第一步冻结证据保存 Controller 磁盘、配置、插件清单和日志记录进程、网络、挂载与环境变量保存当前镜像或安装包摘要导出 Job、Agent、Credentials 元数据和权限配置保留外部代理、Git、制品库和云审计日志。第二步确定凭据边界列出 Jenkins 能接触的所有秘密SCM Token制品上传凭据云 API KeyKubernetes 凭据SSH 私钥签名密钥Webhook Secret数据库和消息系统账号。如果发现 Controller 级可疑代码执行应假设这些秘密可能被读取并按依赖顺序轮换。第三步从可信基线重建 Controller使用已验证的修复版本从受控源恢复配置重新审核插件与摘要不直接复用未知状态下的 Controller 文件系统重建最小权限和网络边界重新注册 Agent 与服务凭据。第四步重新构建关键制品从干净环境和固定源码提交重新构建高价值版本对比二进制摘要文件清单SBOM编译器与依赖版本签名与来源证明容器层差异。无法解释的差异应进入人工调查。第五步重新验证部署将生产运行 Digest 与可信重建结果对账撤销不再可信的签名或发布凭据回滚或替换来源不明的制品检查攻击窗口期间由 Jenkins 发起的基础设施变化。恢复完成的标准不应是“Jenkins 页面重新可用”而应是整条交付链重新获得可验证的信任根。十三、开发团队如何防止同类对象图漏洞1. 建立三维反序列化策略对每个字段同时验证类型是否允许该实际类型 位置它能否出现在当前对象图位置 构造方式是新对象、回引用还是 ID 查找2. 文档根必须有可执行约束类似PersistenceRoot的对象应满足只允许位于文档根嵌套时只能以安全引用出现不允许从外部输入构造第二个单例违规必须导致整个加载失败。3. 数据绑定与行为分离构造器、Setter、readResolve()、数据绑定回调不应执行网络访问文件写入命令执行全局状态注册权限修改路由暴露。先建立纯数据模型完成验证后再进入显式业务阶段。4. 追踪对象的后续消费者代码审计应从反序列化入口继续追踪到Web 路由模板引擎队列和调度器插件系统依赖注入容器脚本或表达式执行器。5. 安全异常不可被兼容逻辑吞掉字段不存在 → 可以考虑兼容 旧数据格式 → 可以迁移 未知可选字段 → 按策略处理 对象图不变量被破坏 → 必须失败并告警十四、DevSecOps 测试矩阵单元测试文档根位于顶层成功文档根作为新嵌套对象失败安全回引用成功ID 占位符成功class覆盖占位符失败第二个单例失败安全异常后部分对象不得保留。插件兼容测试枚举插件定义的PersistenceRoot实现检查插件自定义 Converter验证插件升级与核心升级组合捕获安全异常与被拒绝类型日志不因兼容问题全局放宽白名单。属性测试定义如下属性任意PersistenceRoot子类型只要不是顶层对象或已验证安全引用反序列化必须失败。自动组合普通字段集合Map数组多态字段class与resolves-to属性深层嵌套对象。供应链演练模拟 Controller 失陷验证团队能否枚举所有下游凭据暂停发布从外部日志重建操作链在干净环境重建制品对账生产 Digest在既定时间内轮换关键秘密。十五、长期架构让 Controller 失陷也不能直接等于供应链失陷Controller 最小权限使用非 root 专用账户不挂载 Docker Socket减少宿主机目录挂载将构建执行下沉到隔离、短生命周期 Agent限制 Controller 到生产网络的可达性。凭据最小化使用工作负载身份和短期凭据按 Job、Folder、环境划分权限Controller 不长期保存全局云管理员密钥凭据使用必须产生外部审计记录。发布职责分离CI 生成候选制品独立系统完成安全验证签名服务根据可验证策略授权部署系统只接受满足来源证明的固定 Digest生产发布需要独立身份或审批。不可变与可验证制品仓库禁止覆盖同版本文件镜像部署使用 Digest不依赖可变 Tag保存 SBOM、构建参数和构建器身份将签名记录写入独立透明日志对关键项目建立可复现构建或双构建验证。外部化审计让 Jenkins 无法修改身份代理日志Git 审计日志制品仓库审计日志签名服务日志云与集群审计日志生产准入决策记录。这样即使 Controller 被控制攻击者也难以同时改写全部证据。十六、三个关键认知认知一白名单只是一种局部证明类型可信不代表对象状态、位置、引用关系和后续能力可信。认知二CI RCE 的核心资产不是服务器本身真正的高价值资产是凭据、Agent、发布身份、制品可信度和生产变更能力。认知三修复漏洞与恢复信任是两个项目升级可以关闭入口供应链恢复则需要凭据轮换、可信重建、制品对账和部署验证。十七、总结CVE-2026-84645 的直接根因是 Jenkins 允许本应作为独立文档根的PersistenceRoot对象进入用户提交 XML 的嵌套位置。由于这些对象能够继续参与 Stapler 路由攻击链可以跨越数据与行为边界最终触达具有高权限能力的 Script Console。官方修复没有简单扩大黑名单而是建立了对象图不变量文档根不能作为普通嵌套值合法引用只能使用有限、安全的形式第二个 Jenkins 单例必须被拒绝安全策略违规必须中止加载正常引用必须通过回归测试得到保护。对 CI/CD 团队而言故事还没有在补丁处结束。Jenkins 是软件交付控制平面。它掌握的不是一台服务器而是源码、构建、凭据、签名、制品和部署之间的授权关系。一旦怀疑 Controller 曾被执行任意代码就不能只问“机器清干净了吗”还要问在攻击窗口内这套系统产生的哪些制品、签名和部署仍能被独立证据证明可信只有回答这个问题漏洞修复才真正变成供应链恢复。