Skip to content

CVE-2026-84645: Verified Reproduction

CVE-2026-84645: Jenkins XStream deserialization of nested PersistenceRoot objects leads to RCE via Stapler SECURITY-3972

CVE-2026-84645 is verified against jenkinsci/jenkins · github. Affected versions: Jenkins weekly 2.579 and earlier; Jenkins LTS 2.568.2 and earlier. Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00343.

REPRO-2026-00343 jenkinsci/jenkins · github RCE Vulnerable-path variant Sep 3, 2026 CVE entry .txt
Severity
HIGH
Confidence
HIGH
Reproduced in
44m 4s
Tool calls
502
Spend
$23.72
01 · Overview

What Is CVE-2026-84645?

CVE-2026-84645 is a high-severity RCE vulnerability affecting jenkinsci/jenkins Jenkins weekly 2.579 and earlier; Jenkins LTS 2.568.2 and earlier.. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00343).

02 · Severity & CVSS

CVE-2026-84645 Severity

CVE-2026-84645 is rated high severity.

HIGH threat level
Weakness CWE-94 — Improper Control of Generation of Code ('Code Injection')

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected jenkinsci/jenkins Versions

jenkinsci/jenkins · github versions Jenkins weekly 2.579 and earlier; Jenkins LTS 2.568.2 and earlier. are affected.

How to Reproduce CVE-2026-84645

$ pruva-verify REPRO-2026-00343
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00343/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-84645

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

Authenticated crafted job config.xml with nested SCMTrigger.BuildAction -> FreeStyleBuild -> FreeStyleProject -> Hudson and attacker Groovy posted to the forged Stapler route

Attack chain
  1. POST /job/carrier/config.xml then POST /job/carrier/pollingLog/run/project/parent/scriptText
Variants tested

A distinct Fingerprinter.FingerprintAction#build carrier reaches the same nested PersistenceRoot-to-Stapler Script Console sink and achieves controller RCE on Jenkins 2.579, but the identical alternate trigger is neutralized on Jenkins 2.580; no fix bypass was found.

How the agent worked 777 events · 502 tool calls · 44 min
44 minDuration
502Tool calls
51Reasoning steps
777Events
58Dead-ends
Agent activity over 44 min
Policy
1
Support
19
Repro
370
Judge
36
Variant
346
Verify
1
0:0043:52

Root Cause and Exploit Chain for CVE-2026-84645

Versions: package/component: Jenkins core XStream handling in hudson.util.RobustReflectionConverter, combined with Stapler routing over Jenkins model objects.Fixed: Jenkins weekly 2.580 and LTS 2.568.3.

CVE-2026-84645 (Jenkins SECURITY-3972) is an authenticated remote code execution vulnerability caused by Jenkins XStream deserialization accepting implementations of hudson.model.PersistenceRoot in nested positions of attacker-submitted configuration object graphs. The accepted nested objects remain reflectively traversable by Stapler. In this run, a user limited to Overall/Read, Item/Read, and Item/Configure submitted a job config.xml containing SCMTrigger.BuildAction -> FreeStyleBuild -> FreeStyleProject -> hudson.model.Hudson, where the forged Hudson carried the core AuthorizationStrategy$Unsecured; the attacker then reached that object's doScriptText method through the forged Stapler route and executed Groovy plus the controller-local id command.

  • Affected package/component: Jenkins core XStream handling in hudson.util.RobustReflectionConverter, combined with Stapler routing over Jenkins model objects.
  • Affected versions: Jenkins weekly 2.579 and earlier, and Jenkins LTS 2.568.2 and earlier, per the official advisory.
  • Fixed versions: Jenkins weekly 2.580 and LTS 2.568.3.
  • Risk level: High (official CVSS 3.1 vector AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, score 8.8).
  • Consequences: An authenticated user with job configuration rights can execute arbitrary Groovy and operating-system commands in the Jenkins controller JVM/container security context, enabling full compromise of controller data and behavior.

The runtime used the real Jenkins HTTP/API boundary. Matrix Authorization Strategy Plugin 3.3 was installed only to express the claim's low-privilege account precisely. Before exploitation, the same attacker account received HTTP 403 from the legitimate root /scriptText endpoint in every vulnerable and fixed attempt.

Impact Parity

  • Disclosed/claimed maximum impact: Authenticated remote code execution on the Jenkins controller.
  • Reproduced impact from this run: Authenticated remote code execution on two independent Jenkins 2.579 controller processes. Each attacker-supplied Groovy script created a unique controller-local marker and executed id, returning uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins).
  • Parity: full.
  • Not demonstrated: No claimed impact remains unproven. The proof intentionally stops after a harmless unique file write and id; it does not perform persistence, secret extraction, or destructive actions.

Root Cause

PersistenceRoot identifies Jenkins model objects whose state belongs in an independent top-level persistence document, such as a Jenkins/Hudson singleton, item/job, node, or build. Before the fix, RobustReflectionConverter applied the JEP-200 class allowlist but did not enforce the structural invariant that newly deserialized PersistenceRoot instances must not occur as ordinary nested field values. Consequently, a class could be allowed by identity yet unsafe in its graph position.

The exploit uses only Jenkins core types:

  1. hudson.triggers.SCMTrigger$BuildAction is inserted into the carrier job's persistent actions list. It exposes the Stapler URL name pollingLog and a public getRun() accessor.
  2. Its private run field is deserialized as a nested hudson.model.FreeStyleBuild (a PersistenceRoot).
  3. The build's project field is deserialized as a nested hudson.model.FreeStyleProject (also a PersistenceRoot).
  4. The nested project's parent field is deserialized as a second hudson.model.Hudson/jenkins.model.Jenkins singleton object (also a PersistenceRoot).
  5. That forged root object carries hudson.security.AuthorizationStrategy$Unsecured, so Hudson#doScriptText calls Jenkins._doScript(..., getACL()) with an ACL that allows ADMINISTER.
  6. Stapler reflectively traverses /job/carrier/pollingLog/run/project/parent/scriptText and invokes the forged object's Script Console endpoint, despite the authenticated principal lacking real Jenkins ADMINISTER permission.

The primary fixing commit is 0d731367e08656f8cd1e8275f0e820f97af07fc6 ([SECURITY-3972]), which is present in tag jenkins-2.580 and absent from jenkins-2.579. It adds a PersistenceRoot check in RobustReflectionConverter and throws CriticalXStreamException for unsafe nested instances, with narrow exceptions for references, replacement placeholders, and registered single-value converters. It also adds a second-Jenkins-instance guard and safe replacer logic in Jenkins. Related hardening annotations in the 2.580 release prevent unsafe transient-field reconstruction.

In the fixed runtime, the same config.xml POST completes but serializing the job back shows only <hudson.triggers.SCMTrigger_-BuildAction/>; the forbidden nested Run/Job/Hudson graph is absent. The forged route returns HTTP 404 and no marker is created.

Reproduction Steps

  1. Run bundle/repro/reproduction_steps.sh from any directory. PRUVA_ROOT may optionally identify the bundle root.
  2. The script reads bundle/project_cache_context.json, reuses the prepared cache when available, and otherwise uses bundle-owned fallback paths.
  3. It downloads three pinned plugin dependencies only when absent and verifies their SHA-256 values: Matrix Authorization Strategy 3.3, Ionicons API 94.vcc3065403257, and commons-lang3 API 3.18.0-98.v3a_674c06072d.
  4. It pulls Jenkins by immutable image digest: vulnerable 2.579 (sha256:a7342867…d7412be) and fixed 2.580 (sha256:0e50a5b1…0839b1).
  5. For each of two vulnerable and two fixed clean controller processes, it provisions admin and a low-privilege attacker, creates the carrier freestyle job, verifies that direct Script Console access returns 403, submits the crafted XML over authenticated HTTP, and posts unique Groovy to the forged route.
  6. Vulnerable success requires HTTP 200, the unique marker in both the HTTP response and controller-local marker file, and uid=1000(jenkins) output. Fixed success requires a non-success forged-route status, marker absence, and absence of the nested graph from the resulting job XML.
  7. The script finalizes controller logs, writes bundle/repro/runtime_manifest.json with SHA-256 bindings for all immutable proof artifacts, prints CONFIRMED, and exits 0 only if every assertion passes.

Expected terminal output:

CONFIRMED: SECURITY-3972 achieved authenticated remote command execution on two Jenkins 2.579 controllers; two Jenkins 2.580 controls failed closed.

Evidence

Primary current-run evidence is under bundle/repro/proof/, and every file is bound in bundle/repro/runtime_manifest.json:

  • vulnerable_1.route.response.body and vulnerable_2.route.response.body contain unique markers and controller command output. Latest run excerpts:

    Result: {marker=CVE_2026_84645_vulnerable_1_12521_12184, id=uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins)}
    Result: {marker=CVE_2026_84645_vulnerable_2_12521_18651, id=uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins)}
    
  • vulnerable_1.marker.txt and vulnerable_2.marker.txt are controller-local command markers whose bytes match the per-process values in vulnerable_1.capability_observation.json and vulnerable_2.capability_observation.json.

  • vulnerable_{1,2}.direct_console.headers begin with HTTP/1.1 403 Forbidden, proving the attacker did not already have Script Console access.

  • vulnerable_{1,2}.config.request.txt record the redacted authenticated API request and exact nested graph.

  • vulnerable_{1,2}.route.request.txt record the forged Stapler route and bounded Groovy effect.

  • fixed_{1,2}.route.response.headers begin with HTTP/1.1 404 Not Found.

  • fixed_{1,2}.marker_absent.txt and fixed_{1,2}.negative_control.json record that the corresponding unique marker was not created after the same procedure reached the fixed target.

  • fixed_{1,2}.config.after.xml contain the empty SCMTrigger_-BuildAction and no nested FreeStyleBuild.

  • target_identity.txt records the immutable image digests/image IDs, source tag commits, platform, and architecture.

  • vulnerable_{1,2}.service.log and fixed_{1,2}.service.log show real Jenkins 2.579/2.580 startup and production service initialization.

  • bundle/logs/reproduction_steps.log and bundle/logs/reproduction_steps_second.log show two consecutive successful executions of the final script.

Environment identity:

  • Vulnerable source tag commit: 9095ea3a5c5e7dcd392695a5dd880af1c9910ddf (jenkins-2.579).
  • Fixed source tag commit: 497de4961ad80d97e26bfdeb0d2e40442a84ecb0 (jenkins-2.580).
  • Vulnerable image digest: sha256:a7342867ea33efaacf825229d50b7fc77c144ecada9719ab4e32419f5d7412be.
  • Fixed image digest: sha256:0e50a5b11ac14f3b84e529d725ed3a1c4b17ba16188dfa8d9a0189428b0839b1.
  • Runtime platform: Linux x86-64/amd64, Docker, Jenkins bundled JVM 21.
  • Sanitizers: None.

Recommendations / Next Steps

  • Upgrade Jenkins weekly to 2.580 or later, or Jenkins LTS to 2.568.3 or later.
  • Preserve the fixed PersistenceRoot structural check and its safe-reference exceptions; do not rely on class allowlisting alone for objects with graph-position invariants.
  • Retain the second-singleton guard in Jenkins#readResolve as defense in depth.
  • Review plugin-defined PersistenceRoot implementations and plugin actions that expose routable accessors to root objects.
  • Add regression tests for all configuration-accepting endpoints, including jobs, nodes, builds, users, views, and plugin-defined XML documents. Tests should verify both deserialization rejection/neutralization and that Stapler cannot traverse any partially retained graph.
  • Independently test safe back-references, writeReplace/readResolve placeholders, and single-value converters to avoid compatibility regressions without weakening the structural policy.

Additional Notes

  • Idempotency: Confirmed. The final bundle/repro/reproduction_steps.sh passed twice consecutively. Each execution created two new vulnerable and two new fixed Jenkins controller processes with unique container IDs and markers.
  • Privileges: The attacker account has only Overall/Read, Item/Read, and Item/Configure. A direct /scriptText request is a mandatory 403 negative precondition in all four attempts.
  • Safety: The only command effects are a uniquely named file under the ephemeral controller's /tmp and id; all test containers are removed on completion or interruption.
  • Fixed behavior nuance: The 2.580 endpoint returned HTTP 200 for the configuration update while omitting the prohibited nested values. Security parity is established by the non-routable graph, HTTP 404 forged route, and marker absence rather than by requiring the initial XML POST itself to return an error.
  • Network dependency: A cold run needs access to the pinned Jenkins images and plugin URLs. A warm prepared cache reuses exact plugin bytes, and Docker reuses digest-addressed images.

Variant Analysis & Alternative Triggers for CVE-2026-84645

Fixed: target tested: Jenkins 2.580, source commit 497de4961ad80d97e26bfdeb0d2e40442a84ecb0, official image digest sha256:0e50a5b11ac14f3b84e529d725ed3a1c4b17ba16188dfa8d9a0189428b0839b1.

A materially distinct alternate trigger for CVE-2026-84645 was confirmed on vulnerable Jenkins 2.579, but it is not a fix bypass. Instead of the parent reproduction's SCMTrigger.BuildAction#run carrier and /pollingLog/... route, the alternate uses Fingerprinter.FingerprintAction#build and /fingerprints/run/project/parent/scriptText. A low-privilege authenticated user submitted a forged FreeStyleBuild -> FreeStyleProject -> Hudson graph through job config.xml; Jenkins 2.579 executed Groovy, created a controller-local marker, and ran id. On the exact same payload, Jenkins 2.580 removed the nested build, returned HTTP 404 for the forged route, and created no marker. A newly routable ParametersAction#run candidate and reference/replacer exception-confusion candidates also failed closed on 2.580.

Fix Coverage / Assumptions

The primary fix is commit 0d731367e08656f8cd1e8275f0e820f97af07fc6, included in Jenkins 2.580 commit 497de4961ad80d97e26bfdeb0d2e40442a84ecb0. It establishes this invariant in hudson.util.RobustReflectionConverter#doUnmarshal: a concrete type implementing hudson.model.PersistenceRoot must not be materialized as an ordinary nested field value.

The fix covers normal Jenkins reflective XStream conversion globally rather than matching one endpoint or gadget. It throws CriticalXStreamException before unmarshalField and preserves narrow compatibility cases:

  1. reference= resolves an already-deserialized graph object.
  2. A non-root resolves-to= placeholder without a class= override performs a registry lookup in readResolve.
  3. A concrete class with a registered SingleValueConverter consumes a scalar identifier and returns an existing object.

XStream2#setupConverters supplies the actual converter lookup. Jenkins#readResolve rejects a second singleton; Jenkins#writeReplace emits a safe replacer when nested. Queue load handles a critical violation by starting empty. Related 2.580 changes annotate transient back-reference fields such as SCMTrigger.BuildAction#run, Fingerprinter.FingerprintAction#build, ParametersAction#run, Run#project, and Node#parent with @XStreamNotDeserializable.

The fix assumes custom converters, replacement classes, and scalar converters preserve the same identity-only semantics. A plugin parser or wholly custom converter that manually constructs a root is not directly inspected by this reflective field check. No such same-boundary bypass was found here. Full details are in bundle/vuln_variant/patch_analysis.md.

Variant / Alternate Trigger

Confirmed alternate path: FingerprintAction
  • Configuration entry point: authenticated POST /job/carrier/config.xml.
  • Different carrier: hudson.tasks.Fingerprinter$FingerprintAction.
  • Different field: transient build, whose concrete value is a nested hudson.model.FreeStyleBuild.
  • Object graph: FingerprintAction#build -> FreeStyleBuild#project -> FreeStyleProject#parent -> hudson.model.Hudson.
  • Different Stapler route: authenticated POST /job/carrier/fingerprints/run/project/parent/scriptText.
  • Sink: forged Hudson#doScriptText; AuthorizationStrategy$Unsecured on the forged root makes its local ACL check pass.

This is materially distinct from the parent SCMTrigger.BuildAction#run and URL name pollingLog. The two paths converge only after their separate Run-bearing actions/getters reach Run#getProject. The equivalence record is bundle/vuln_variant/root_cause_equivalence.json.

Candidate B: ParametersAction

ParametersAction#run was tested through /job/carrier/parameters/run/project/parent/scriptText. Jenkins 2.579 contains the transient field but no public getRun, so the route is not traversable. Jenkins 2.580 adds a getter but simultaneously marks the field non-deserializable, and the global root policy removes the forged value. Both targets returned HTTP 404 with no marker. This candidate probes a genuinely new fixed-version routing surface but does not produce a bypass.

Candidate C: fix exception confusion

A reference= node combined with an incompatible class= and a nonexistent Run$Replacer identifier tested the two broad compatibility assumptions. Neither case created a root graph on either target; both candidate action routes returned HTTP 404 and all markers remained absent. Only three candidates are reported because the source scan found three materially different classes: an alternate core Run-bearing action, a newly routable fixed-version action, and a direct exception-assumption test. Additional method names over the same graph would not constitute new variants.

  • Package/component: Jenkins core XStream persistence (XStream2 / RobustReflectionConverter) and Stapler routing over model objects.
  • Vulnerable target tested: Jenkins 2.579, source commit 9095ea3a5c5e7dcd392695a5dd880af1c9910ddf, official image digest sha256:a7342867ea33efaacf825229d50b7fc77c144ecada9719ab4e32419f5d7412be.
  • Fixed target tested: Jenkins 2.580, source commit 497de4961ad80d97e26bfdeb0d2e40442a84ecb0, official image digest sha256:0e50a5b11ac14f3b84e529d725ed3a1c4b17ba16188dfa8d9a0189428b0839b1.
  • Risk on affected releases: High; an authenticated user with Item/Configure, not Overall/Administer, can execute arbitrary controller Groovy/commands.
  • Fixed-target consequence: None reproduced. The tested alternate route is non-routable and has no process effect on 2.580.

The trust boundary matches Jenkins' security policy. Administrators already having Script Console access are excluded as a vulnerability case, so the script provisions an attacker with only Overall/Read, Item/Read, and Item/Configure and requires direct /scriptText to return HTTP 403. The XML and Groovy arrive over authenticated HTTP. This is not a local administrator loading their own file.

Impact Parity

  • Disclosed/claimed maximum impact: Authenticated remote code execution on the Jenkins controller.
  • Reproduced impact from this alternate trigger: Full controller command execution on Jenkins 2.579. Groovy created a unique controller-local marker, and id returned uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins).
  • Parity: full for the confirmed alternate trigger on the affected release; none on the fixed variant target.
  • Not demonstrated: No fixed-version command execution, persistence, credential extraction, lateral movement, or destructive action. The proof stops at a temporary marker and id.

Root Cause

JEP-200 validates which classes XStream may deserialize, but Jenkins 2.579 does not validate whether an allowed PersistenceRoot appears in a safe graph position. FingerprintAction#build can therefore contain a newly constructed FreeStyleBuild; that build can contain a forged project; and that project can contain a second forged Hudson with attacker-selected authorization state. Actionable#getDynamic locates the action by URL name, and Stapler follows FingerprintAction#getRun, Run#getProject, and AbstractItem#getParent to the forged script endpoint.

The parent and alternate triggers have the same attacker-controlled source, structural defect, Run -> Job -> Hudson graph, Stapler sink, permission-boundary crossing, and RCE impact. The material difference is the core carrier/field and its URL name. Jenkins 2.580 closes both the generic root-position defect and this specific transient field, so the alternate does not bypass the fix.

Reproduction Steps

  1. Run bundle/vuln_variant/reproduction_steps.sh from any directory. Set PRUVA_ROOT only if the bundle root cannot be inferred.

  2. The script obtains pinned Matrix Authorization Strategy 3.3 and its pinned dependencies from the prepared cache or official update URLs and validates SHA-256 values.

  3. It pulls immutable Jenkins 2.579 and 2.580 image digests and records image/source identity in bundle/logs/vuln_variant/fixed_version.txt.

  4. It starts the vulnerable controller first and the fixed controller second. Each receives a fresh security realm, a low-privilege attacker, and a carrier job.

  5. It requires direct Script Console access to return HTTP 403, then exercises the FingerprintAction alternate graph, ParametersAction path, and exception-confusion payloads.

  6. It captures response headers/bodies, resulting job XML, controller-local marker presence, controller logs, and structured observations under bundle/logs/vuln_variant/proof/.

  7. Expected output is:

    NO FIX BYPASS: the distinct FingerprintAction carrier executes on vulnerable Jenkins 2.579 but is blocked on Jenkins 2.580; the newly routable ParametersAction and reference/replacer exception-confusion candidates also fail closed.
    
  8. The script exits 1 by design because the validated alternate trigger works only on the vulnerable release. Exit 0 is reserved for a fixed-version bypass; exit 2 means the matrix was inconclusive.

Evidence

  • bundle/logs/vuln_variant/proof/vulnerable.fingerprint.route.response.body:

    Result: {marker=CVE_2026_84645_VARIANT_vulnerable_fingerprint_28611_d5120ae57236, id=uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins)}
    
  • vulnerable.fingerprint.observation.json: route HTTP 200 and marker_present: true.

  • vulnerable.fingerprint.marker.txt: the same unique marker read from inside the controller container.

  • fixed.fingerprint.observation.json: route HTTP 404 and marker_present: false.

  • fixed.fingerprint.config.after.xml: preserves only <hudson.tasks.Fingerprinter_-FingerprintAction/>; no nested build/project/Hudson remains.

  • fixed.fingerprint.marker_absent.txt: controller marker did not exist.

  • vulnerable.parameters.observation.json and fixed.parameters.observation.json: both route HTTP 404 and no marker.

  • fixed.exceptions.config.after.xml: reference/replacer candidate leaves only empty actions and no root graph.

  • vulnerable.exceptions.*.marker_absent.txt and fixed.exceptions.*.marker_absent.txt: all exception-candidate markers absent.

  • vulnerable.direct_console.headers and fixed.direct_console.headers: direct root Script Console returned HTTP 403.

  • vulnerable.service.log and fixed.service.log: real controller startup as versions 2.579 and 2.580.

  • bundle/logs/vuln_variant/fixed_version.txt: exact commits, image digests, image IDs, architecture, OS, and fix commit.

  • bundle/vuln_variant/runtime_manifest.json: SHA-256 binding for proof files.

  • bundle/logs/vuln_variant/reproduction_first.log and reproduction_second.log: two consecutive complete executions, each ending with expected exit code 1.

Recommendations / Next Steps

  • Upgrade weekly Jenkins to 2.580 or later, or LTS to 2.568.3 or later.
  • Preserve the global nested-PersistenceRoot check and propagation of CriticalXStreamException; do not rely only on the targeted @XStreamNotDeserializable annotations.
  • Retain the Jenkins singleton readResolve guard and replacement behavior as defense in depth.
  • Audit plugin-defined XStream converters, SingleValueConverter implementations, and replacement classes. Any nested PersistenceRoot result must come from an identity/registry lookup and must not accept child state.
  • Require plugin-defined independent persistence roots to implement PersistenceRoot so the global policy can recognize them.
  • Add regression tests for plugin actions exposing routable root getters and for partial graph state after a critical conversion failure.
  • Keep tests for reference=, resolves-to=, and scalar Item/Run queue references so compatibility changes cannot weaken the invariant unnoticed.

Additional Notes

  • Classification: Confirmed alternate vulnerable-version trigger; no Jenkins 2.580 bypass.
  • Idempotency: Confirmed. The final script was executed twice and both runs completed normally with expected exit code 1.
  • Safety: Every controller is ephemeral. The only command effects are a unique file under /tmp and id; containers are removed on exit/interruption.
  • Endpoint nuance: The fixed job configuration update may return HTTP 200 while safely omitting the forbidden nested fields. Security is established by graph removal, route HTTP 404, and marker absence rather than requiring the config POST itself to fail.
  • Scope limitation: This run tested Jenkins core carriers and fix exceptions. It did not install arbitrary third-party plugins to search for separate plugin-specific custom converters; such a bug would need independent same-root-cause and same-trust-boundary validation.

CVE-2026-84645 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:002:06
0:00
session startedgpt-5.6-sol · CVE-2026-84645 · REPRO-20
0:13
0:14
web search
0:15
web search
0:24
0:25
0:35
web search
0:36
web search
0:38
web search
0:40
1:19
1:19
1:19
1:19
extract_facts
no facts extracted
1:28
1:28
supportclaim_contract
1:36
1:37
1:37
1:37
1:37
1:49
1:49
## Summary

CVE-2026-84645 is reported to affect Jenkins' XStream/JEP-200 deserialization and Stapler routing: a structurally invalid nested `hudson.model.PersistenceRoot` value can allegedly be submitted through a job configuration endpoint, retained as a forged object graph, and traversed by Stapler to reach controller functionality that should not be exposed through that graph. Runtime validation is in progress against the claimed Jenkins API boundary.

## Impact

- **Affected component:** Jenkins controller core, specifically XML configuration deserialization and Stapler object routing.
- **Claimed affected versions:** Jenkins weekly through 2.579 and LTS through 2.568.2.
- **Risk:** High. The submitted claim states that an authenticated low-privilege user with job configuration permission can execute arbitrary Groovy/system commands on the Jenkins controller.

## Impact Parity

- **Disclosed/claimed maximum impact:** Remote code execution on the Jenkins controller.
- **Reproduced impact from this run:** Runtime validation is pending.
- **Parity:** `none` (provisional until the required current-run experiment completes).
- **Not demonstrated:** Current-run API reachability, for… [truncated]
2:03
2:03
2:05
web search
2:06

Artifacts and Evidence for CVE-2026-84645

Scripts, logs, diffs, and output captured during the reproduction.

bundle/logs/reproduction_steps.log0.4 KB
bundle/logs/reproduction_steps_second.log0.4 KB
bundle/logs/vuln_variant/fixed_version.txt0.9 KB
bundle/logs/vuln_variant/proof/fixed.container_id.txt0.1 KB
bundle/logs/vuln_variant/proof/fixed.direct_console.body19.5 KB
bundle/logs/vuln_variant/proof/fixed.direct_console.headers1.5 KB
bundle/logs/vuln_variant/proof/fixed.exceptions.config.after.xml0.7 KB
bundle/logs/vuln_variant/proof/fixed.exceptions.config.response.body0.0 KB
bundle/logs/vuln_variant/proof/fixed.exceptions.config.response.headers0.4 KB
bundle/logs/vuln_variant/proof/fixed.exceptions.fingerprints.body18.9 KB
bundle/logs/vuln_variant/proof/fixed.exceptions.fingerprints.headers1.2 KB
bundle/logs/vuln_variant/proof/fixed.exceptions.fingerprints.marker_absent.txt0.1 KB
bundle/logs/vuln_variant/proof/fixed.exceptions.parameters.body18.9 KB
bundle/logs/vuln_variant/proof/fixed.exceptions.parameters.headers1.2 KB
bundle/logs/vuln_variant/proof/fixed.exceptions.parameters.marker_absent.txt0.1 KB
bundle/logs/vuln_variant/proof/fixed.fingerprint.config.after.xml0.6 KB
bundle/logs/vuln_variant/proof/fixed.fingerprint.config.request.txt1.4 KB
bundle/logs/vuln_variant/proof/fixed.fingerprint.config.response.body0.0 KB
bundle/logs/vuln_variant/proof/fixed.fingerprint.config.response.headers0.4 KB
bundle/logs/vuln_variant/proof/fixed.fingerprint.marker_absent.txt0.1 KB
bundle/logs/vuln_variant/proof/fixed.fingerprint.observation.json0.2 KB
bundle/logs/vuln_variant/proof/fixed.fingerprint.route.request.txt0.3 KB
bundle/logs/vuln_variant/proof/fixed.fingerprint.route.response.body18.9 KB
bundle/logs/vuln_variant/proof/fixed.fingerprint.route.response.headers1.2 KB
bundle/logs/vuln_variant/proof/fixed.health.json0.7 KB
bundle/logs/vuln_variant/proof/fixed.parameters.config.after.xml0.7 KB
bundle/logs/vuln_variant/proof/fixed.parameters.config.request.txt1.3 KB
bundle/logs/vuln_variant/proof/fixed.parameters.config.response.body0.0 KB
bundle/logs/vuln_variant/proof/fixed.parameters.config.response.headers0.4 KB
bundle/logs/vuln_variant/proof/fixed.parameters.marker_absent.txt0.1 KB
bundle/logs/vuln_variant/proof/fixed.parameters.observation.json0.2 KB
bundle/logs/vuln_variant/proof/fixed.parameters.route.request.txt0.2 KB
bundle/logs/vuln_variant/proof/fixed.parameters.route.response.body18.9 KB
bundle/logs/vuln_variant/proof/fixed.parameters.route.response.headers1.2 KB
bundle/logs/vuln_variant/proof/fixed.service.log4.7 KB
bundle/logs/vuln_variant/proof/vulnerable.container_id.txt0.1 KB
bundle/logs/vuln_variant/proof/vulnerable.direct_console.body19.5 KB
bundle/logs/vuln_variant/proof/vulnerable.direct_console.headers1.5 KB
bundle/logs/vuln_variant/proof/vulnerable.exceptions.config.after.xml0.7 KB
bundle/logs/vuln_variant/proof/vulnerable.exceptions.config.response.body0.0 KB
bundle/logs/vuln_variant/proof/vulnerable.exceptions.config.response.headers0.4 KB
bundle/logs/vuln_variant/proof/vulnerable.exceptions.fingerprints.body18.9 KB
bundle/logs/vuln_variant/proof/vulnerable.exceptions.fingerprints.headers1.2 KB
bundle/logs/vuln_variant/proof/vulnerable.exceptions.fingerprints.marker_absent.txt0.1 KB
bundle/logs/vuln_variant/proof/vulnerable.exceptions.parameters.body18.9 KB
bundle/logs/vuln_variant/proof/vulnerable.exceptions.parameters.headers1.2 KB
bundle/logs/vuln_variant/proof/vulnerable.exceptions.parameters.marker_absent.txt0.1 KB
bundle/logs/vuln_variant/proof/vulnerable.fingerprint.config.after.xml0.6 KB
bundle/logs/vuln_variant/proof/vulnerable.fingerprint.config.request.txt1.4 KB
bundle/logs/vuln_variant/proof/vulnerable.fingerprint.config.response.body0.0 KB
bundle/logs/vuln_variant/proof/vulnerable.fingerprint.config.response.headers0.4 KB
bundle/logs/vuln_variant/proof/vulnerable.fingerprint.marker.txt0.1 KB
bundle/logs/vuln_variant/proof/vulnerable.fingerprint.observation.json0.2 KB
bundle/logs/vuln_variant/proof/vulnerable.fingerprint.route.request.txt0.3 KB
bundle/logs/vuln_variant/proof/vulnerable.fingerprint.route.response.body0.1 KB
bundle/logs/vuln_variant/proof/vulnerable.fingerprint.route.response.headers0.4 KB
bundle/logs/vuln_variant/proof/vulnerable.health.json0.7 KB
bundle/logs/vuln_variant/proof/vulnerable.parameters.config.after.xml0.7 KB
bundle/logs/vuln_variant/proof/vulnerable.parameters.config.request.txt1.3 KB
bundle/logs/vuln_variant/proof/vulnerable.parameters.config.response.body0.0 KB
bundle/logs/vuln_variant/proof/vulnerable.parameters.config.response.headers0.4 KB
bundle/logs/vuln_variant/proof/vulnerable.parameters.marker_absent.txt0.1 KB
bundle/logs/vuln_variant/proof/vulnerable.parameters.observation.json0.2 KB
bundle/logs/vuln_variant/proof/vulnerable.parameters.route.request.txt0.3 KB
bundle/logs/vuln_variant/proof/vulnerable.parameters.route.response.body18.9 KB
bundle/logs/vuln_variant/proof/vulnerable.parameters.route.response.headers1.2 KB
bundle/logs/vuln_variant/proof/vulnerable.service.log6.3 KB
bundle/logs/vuln_variant/reproduction_first.log0.2 KB
bundle/logs/vuln_variant/reproduction_second.log0.2 KB
bundle/repro/proof/fixed_1.config.after.xml0.6 KB
bundle/repro/proof/fixed_1.config.response.body0.0 KB
bundle/repro/proof/fixed_1.config.response.headers0.4 KB
bundle/repro/proof/fixed_1.container_id.txt0.1 KB
bundle/repro/proof/fixed_1.direct_console.body19.5 KB
bundle/repro/proof/fixed_1.direct_console.headers1.5 KB
bundle/repro/proof/fixed_1.health.json0.7 KB
bundle/repro/proof/fixed_1.marker_absent.txt0.1 KB
bundle/repro/proof/fixed_1.route.request.txt0.3 KB
bundle/repro/proof/fixed_1.route.response.body18.9 KB
bundle/repro/proof/fixed_1.service.log4.3 KB
bundle/repro/proof/fixed_2.config.after.xml0.6 KB
bundle/repro/proof/fixed_2.config.request.txt1.3 KB
bundle/repro/proof/fixed_2.config.response.body0.0 KB
bundle/repro/proof/fixed_2.config.response.headers0.4 KB
bundle/repro/proof/fixed_2.container_id.txt0.1 KB
bundle/repro/proof/fixed_2.direct_console.body19.5 KB
bundle/repro/proof/fixed_2.direct_console.headers1.5 KB
bundle/repro/proof/fixed_2.health.json0.7 KB
bundle/repro/proof/fixed_2.marker_absent.txt0.1 KB
bundle/repro/proof/fixed_2.negative_control.json0.2 KB
bundle/repro/proof/fixed_2.route.request.txt0.3 KB
bundle/repro/proof/fixed_2.route.response.body18.9 KB
bundle/repro/proof/fixed_2.route.response.headers1.2 KB
bundle/repro/proof/fixed_2.service.log4.3 KB
bundle/repro/proof/target_identity.txt0.6 KB
bundle/repro/proof/vulnerable_1.config.after.xml0.6 KB
bundle/repro/proof/vulnerable_1.config.response.body0.0 KB
bundle/repro/proof/vulnerable_1.config.response.headers0.4 KB
bundle/repro/proof/vulnerable_1.container_id.txt0.1 KB
bundle/repro/proof/vulnerable_1.direct_console.body19.5 KB
bundle/repro/proof/vulnerable_1.direct_console.headers1.5 KB
bundle/repro/proof/vulnerable_1.health.json0.7 KB
bundle/repro/proof/vulnerable_1.route.response.headers0.4 KB
bundle/repro/proof/vulnerable_2.capability_observation.json0.2 KB
bundle/repro/proof/vulnerable_2.config.after.xml0.6 KB
bundle/repro/proof/vulnerable_2.config.request.txt1.3 KB
bundle/repro/proof/vulnerable_2.config.response.body0.0 KB
bundle/repro/proof/vulnerable_2.config.response.headers0.4 KB
bundle/repro/proof/vulnerable_2.container_id.txt0.1 KB
bundle/repro/proof/vulnerable_2.direct_console.body19.5 KB
bundle/repro/proof/vulnerable_2.direct_console.headers1.5 KB
bundle/repro/proof/vulnerable_2.health.json0.7 KB
bundle/repro/proof/vulnerable_2.route.request.txt0.3 KB
bundle/repro/proof/vulnerable_2.route.response.body0.1 KB
bundle/repro/proof/vulnerable_2.route.response.headers0.4 KB
bundle/repro/proof/vulnerable_2.service.log4.3 KB
bundle/repro/rca_report.md10.8 KB
bundle/repro/reproduction_steps.sh14.8 KB
bundle/repro/runtime_manifest.json10.2 KB
bundle/repro/validation_verdict.json1.4 KB
bundle/vuln_variant/patch_analysis.md10.9 KB
bundle/vuln_variant/rca_report.md12.5 KB
bundle/vuln_variant/reproduction_steps.sh18.4 KB
bundle/vuln_variant/root_cause_equivalence.json2.0 KB
bundle/vuln_variant/runtime_manifest.json13.9 KB
bundle/vuln_variant/source_identity.json1.1 KB
bundle/vuln_variant/validation_verdict.json3.7 KB
bundle/vuln_variant/variant_manifest.json4.2 KB
08 · How to Fix

How to Fix CVE-2026-84645

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-84645 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-84645

Is CVE-2026-84645 exploitable?

Yes. Pruva independently reproduced CVE-2026-84645 in jenkinsci/jenkins and verified the exploit fires end-to-end in a sandboxed environment. A runnable proof-of-concept script and the full agent transcript are on this page (reproduction REPRO-2026-00343).

How severe is CVE-2026-84645?

CVE-2026-84645 is rated high severity.

What type of vulnerability is CVE-2026-84645?

CVE-2026-84645 is classified as CWE-94 (Improper Control of Generation of Code ('Code Injection')), a RCE vulnerability.

Which versions of jenkinsci/jenkins are affected by CVE-2026-84645?

jenkinsci/jenkins Jenkins weekly 2.579 and earlier; Jenkins LTS 2.568.2 and earlier. is affected by CVE-2026-84645.

How can I reproduce CVE-2026-84645?

Pruva provides a verified reproduction script on this page. Download it and run it inside an isolated environment such as a container or virtual machine — never against production. The reproduction was confirmed end-to-end by Pruva's automated agents.

Is the CVE-2026-84645 reproduction verified?

Yes. Pruva reproduced CVE-2026-84645 with high confidence in a sandboxed environment, capturing the full agent transcript and artifacts as evidence.
11 · References

References for CVE-2026-84645

Authoritative sources for CVE-2026-84645 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.