CVE-2026-84647: Verified Reproduction
CVE-2026-84647: Jenkins Stapler form binding instantiates arbitrary config types → RCE SECURITY-3966
CVE-2026-84647 is verified against jenkinsci/jenkins · github. Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00349.
What Is CVE-2026-84647?
CVE-2026-84647 is a high-severity RCE vulnerability affecting jenkinsci/jenkins. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00349).
CVE-2026-84647 Severity
CVE-2026-84647 is rated high severity.
High — serious impact or readily exploitable. Prioritize remediation.
How to Reproduce CVE-2026-84647
pruva-verify REPRO-2026-00349 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00349/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-84647
- 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
Authenticated multipart structured form data whose ListView columns stapler-class-bag selects StashedFileParameterValue, including an uploaded Groovy file and traversal name
- POST /user/alice/my-views/createView
- Stapler RequestImpl collection bindJSON
- incompatible @DataBoundConstructor
- JENKINS_HOME/init.groovy.d write
- controller restart
How the agent worked
Root Cause and Exploit Chain for CVE-2026-84647
Jenkins 2.579 bundles Stapler 2107.v8dfcb_e8ed317, whose structured-form collection binder trusts every class name encoded as a key beneath stapler-class-bag. It loads and instantiates the requested class without first checking that the class is a subtype of the collection's declared item type. An authenticated user with only Overall/Read can reach the personal My Views creation endpoint and submit a ListView.columns collection that names an unrelated configuration class. In this run, the attacker selected io.jenkins.plugins.file_parameters.StashedFileParameterValue; its data-bound constructor consumed an uploaded file and an attacker-controlled traversal name, placing attacker Groovy under JENKINS_HOME/init.groovy.d. On an ordinary Jenkins restart, the real controller executed the Groovy startup hook and created a unique command-execution marker. Jenkins 2.580 reached the same endpoint but ignored the incompatible class before construction.
- Affected component: Jenkins core's bundled Stapler structured form-binding implementation, specifically collection conversion in
org.kohsuke.stapler.RequestImpl.TypePair.convertJSON. - Affected versions: Stapler 2107.v8dfcb_e8ed317 and earlier, except 2088.2093.vd7c3e58008a_6; included in Jenkins weekly 2.579 and earlier and Jenkins LTS 2.568.2 and earlier. The issue is fixed in Jenkins weekly 2.580 and LTS 2.568.3.
- Tested vulnerable target: Immutable image
jenkins/jenkins@sha256:a7342867ea33efaacf825229d50b7fc77c144ecada9719ab4e32419f5d7412be(Jenkins 2.579, source commit9095ea3a5c5e7dcd392695a5dd880af1c9910ddf). - Tested fixed target: Immutable image
jenkins/jenkins@sha256:0e50a5b11ac14f3b84e529d725ed3a1c4b17ba16188dfa8d9a0189428b0839b1(Jenkins 2.580, source commit497de4961ad80d97e26bfdeb0d2e40442a84ecb0). - Risk: High. A network attacker with only Overall/Read can invoke constructors and setters of configuration-related classes outside the intended destination type. Impact depends on available data-bound gadget classes. With File Parameter Plugin 425.v3fa_801681b_5e, the run demonstrated a controller file-write gadget and persistent controller-side code execution after restart. The low-privilege user was denied direct Script Console access.
Impact Parity
- Disclosed/claimed maximum impact: Remote code execution on the Jenkins controller.
- Reproduced impact from this run: Full controller code execution. Two independent low-privilege HTTP submissions placed attacker-selected Groovy in
JENKINS_HOME/init.groovy.d; after restarting each controller, Jenkins' realGroovyHookScriptexecuted it and created a unique marker. - Parity:
full. - Not demonstrated: Immediate, restart-free command execution was not required or demonstrated. The proven chain requires a later controller restart and the installed File Parameter Plugin 425 gadget. No sanitizer, direct unit harness, administrative token, Script Console, or Item/Configure permission was used for the attack.
Root Cause
For collection-valued form fields, Stapler recognizes a JSON object containing "stapler-class-bag": true. In vulnerable RequestImpl.TypePair.convertJSON, it iterates over all object entries, converts each entry key from JSON-safe - escaping back to ., and performs:
Class<?> itemType = cl.loadClass(className);
l.add(bindJSON(itemType, (JSONObject) v));
Although the collection lister carries the expected element class as l.itemType, the vulnerable code never checks l.itemType.isAssignableFrom(itemType) before recursively binding and constructing the attacker-selected class. Type incompatibility is discovered only later, if at all; constructor and setter side effects have already occurred. The low-privilege personal-view endpoint is exploitable because creating a personal view is permitted with Overall/Read and ListView has a data-bound List<ListViewColumn> property.
The security fix is Stapler commit b49b34c07103fb238a566e51b531729f1b68e73d (SECURITY-3915). It changes class loading to:
Class<?> itemType = cl.loadClass(className).asSubclass(l.itemType);
and catches ClassCastException together with ClassNotFoundException. Consequently, Jenkins 2.580 ignores StashedFileParameterValue when binding ListView.columns because it is not a ListViewColumn, so its constructor never receives the upload and cannot create the startup hook.
Reproduction Steps
- Run
bundle/repro/reproduction_steps.shwith Bash and a working Docker daemon. - The script reads
bundle/project_cache_context.jsonwhen available, otherwise usesbundle/artifacts/jenkins-security-3915; downloads checksum-pinned plugins; and uses immutable Jenkins 2.579 and 2.580 image digests. - For each target, it starts clean controller processes with matrix authorization granting
aliceonly Overall/Read. It proves the restriction withGET /scriptreturning HTTP 403. - It sends an authenticated multipart
POST /user/alice/my-views/createView. The submittedListView.columns.stapler-class-bagnamesStashedFileParameterValue, and the uploaded file is attacker Groovy whose constructor argument places it at../../init.groovy.d/cve84647.groovyrelative to the gadget's temporary directory. - It restarts the real Jenkins controller and checks for a unique target-local marker written by that Groovy.
- The script performs two clean Jenkins 2.579 attempts and two clean Jenkins 2.580 controls, emits
bundle/repro/runtime_manifest.json, verifies every bound artifact hash, and exits 0 only for the expected vulnerable/fixed divergence. - Expected terminal output includes:
CONFIRMED vulnerable attempt 1: low-privilege request executed attacker Groovy after restart
CONFIRMED vulnerable attempt 2: low-privilege request executed attacker Groovy after restart
CONFIRMED fixed attempt 1: target path reached but incompatible class constructor did not run
CONFIRMED fixed attempt 2: target path reached but incompatible class constructor did not run
CVE-2026-84647 CONFIRMED: two low-privilege production HTTP attempts achieved controller code execution; two fixed controls failed closed.
Evidence
bundle/repro/runtime_manifest.json: strict runtime manifest with 42 immutable proof artifacts and SHA-256 bindings.bundle/repro/proof/vulnerable_1.request.jsonandvulnerable_2.request.json: attacker request shape crossing the actual Jenkins HTTP endpoint.bundle/repro/proof/vulnerable_1.response.headersandvulnerable_2.response.headers: endpoint responses.bundle/repro/proof/vulnerable_1.privilege.txtandvulnerable_2.privilege.txt:GET /script HTTP 403, proving the attacker did not have Overall/Administer.bundle/repro/proof/vulnerable_1.result.txtandvulnerable_2.result.txt: each recordspayload_script_present=trueandmarker_present=true.bundle/repro/proof/vulnerable_1.marker.txtandvulnerable_2.marker.txt: unique controller-local command markers.bundle/repro/proof/vulnerable_1.service.logandvulnerable_2.service.log: real Jenkins lifecycle logs. Each includesStarting version 2.579andExecuting /var/jenkins_home/init.groovy.d/cve84647.groovyafter restart.bundle/repro/proof/fixed_1.result.txtandfixed_2.result.txt: same endpoint reached on Jenkins 2.580, butpayload_script_present=falseandmarker_present=false.bundle/repro/proof/fixed_1.negative_control.jsonandfixed_2.negative_control.json: explicit target-reached fixed observations.bundle/repro/proof/fixed_1.service.logandfixed_2.service.log: Jenkins 2.580 starts and restarts without executing the attacker hook.bundle/logs/reproduction_steps.log: concise final-run diagnostic summary.
A final vulnerable log excerpt is:
Starting version 2.579
Executing /var/jenkins_home/init.groovy.d/cve84647.groovy
Jenkins is fully up and running
The corresponding fixed result is:
POST /user/alice/my-views/createView HTTP 302
payload_script_present=false
marker_present=false
The final script was executed twice consecutively. Both complete runs exited 0 (approximately 63 seconds and 58 seconds) and reproduced two vulnerable executions plus two fixed controls on each run.
Recommendations / Next Steps
- Upgrade Jenkins weekly to 2.580 or newer, or Jenkins LTS to 2.568.3 or newer.
- Ensure the bundled Stapler version contains commit
b49b34c07103fb238a566e51b531729f1b68e73dor an equivalent backport. - Independently upgrade plugins fixed in the 2026-09-02 advisory. In particular, update File Parameter Plugin to 433.va_0b_80359d54d or newer; this removes the traversal primitive used in the demonstrated composition.
- Audit
@DataBoundConstructorand@DataBoundSettermethods for side effects and permission checks. Constructors should not write files, initiate network connections, modify global state, or execute commands merely as a consequence of binding. - Add regression tests for every structured-form collection syntax, especially
stapler-class-bag, proving that unrelated classes are rejected before constructors or setters execute. - Monitor
JENKINS_HOME/init.groovy.d, plugin configuration files, and unexpected controller restarts for evidence of persistence. Rotate secrets if compromise is suspected.
Additional Notes
- Idempotency: Confirmed. The final reproducer passed twice consecutively and uses isolated Docker volumes, unique container names, unique marker values, bounded health checks, and cleanup traps.
- Runtime boundary: Production Jenkins HTTP/Stapler endpoint, not a direct parser call or unit harness.
- Sanitizers: None used.
- Exploit preconditions: Authenticated Overall/Read access; an available configuration-related data-bound gadget (File Parameter Plugin 425 in this proof); write access of the Jenkins process to its own home; and a later controller restart for startup-hook execution.
- Endpoint behavior: Both vulnerable and fixed versions return HTTP 302 because the outer personal ListView remains valid. Security is proven by the constructor side effect and marker divergence, not status code alone.
- Scope limitation: The demonstrated file gadget comes from an affected plugin. The root vulnerability itself is in Stapler and permits incompatible class selection; other installed classes may provide different consequences.
CVE-2026-84647 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.
Artifacts and Evidence for CVE-2026-84647
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-84647
FAQ: CVE-2026-84647
Is CVE-2026-84647 exploitable?
How severe is CVE-2026-84647?
What type of vulnerability is CVE-2026-84647?
How can I reproduce CVE-2026-84647?
Is the CVE-2026-84647 reproduction verified?
References for CVE-2026-84647
Authoritative sources for CVE-2026-84647 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.