# REPRO-2026-00351: Jenkins remember-me session fixation — attacker-plantable cookie value not rotated after login (SECURITY-4069) ## Summary Status: published Severity: high CVSS: Unknown CWE: CWE-384 (Session Fixation) Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00351 CVE: CVE-2026-84652 ## Package Name: jenkinsci/jenkins Ecosystem: github Affected: Jenkins weekly 2.579 and earlier; Jenkins LTS 2.568.2 and earlier. Fixed: Unknown ## Root Cause ## Summary CVE-2026-84652 (Jenkins SECURITY-4016) is a session-fixation vulnerability in Jenkins's persistent "Keep me signed in" authentication flow. Jenkins 2.579 authenticates a valid remember-me token without applying a session-authentication strategy, so an anonymous `JSESSIONID` planted before auto-login remains the authenticated session identifier. An attacker who knows and can plant that cookie can replay it after the victim's remember-me authentication and act as the victim. Fresh production-path tests against real Jenkins HTTP controllers reproduced this behavior twice on 2.579 and showed that 2.580 rotates the identifier and blocks replay in two fixed controls. ## Impact - **Affected component:** Jenkins core, `hudson.security.SecurityRealm` filter-chain construction around Spring Security's `RememberMeAuthenticationFilter`. - **Affected versions:** Jenkins weekly through 2.579 and Jenkins LTS through 2.568.2. - **Fixed versions:** Jenkins weekly 2.580 and Jenkins LTS 2.568.3. - **Risk:** High. An unauthenticated attacker who can plant a same-site Jenkins session cookie (for example, from a sibling subdomain) and induce the victim's later remember-me auto-login can reuse the known session as the victim. The attack requires victim interaction and a cookie-planting condition but does not require the attacker to know the victim's credentials or remember-me token. ## Impact Parity - **Disclosed/claimed maximum impact:** Authorization bypass through takeover of a victim-authenticated Jenkins session. - **Reproduced impact:** Full authorization bypass. In each vulnerable attempt, a separate attacker client sent only the originally known anonymous `JSESSIONID` and received HTTP 200 from `/me/api/json` with `id: "victim"` after the victim browser state triggered remember-me authentication. - **Parity:** `full`. - **Not demonstrated:** Cookie planting through an actual hostile sibling DNS origin was not recreated. Instead, the test places the attacker's server-issued anonymous cookie into a separate victim cookie jar, which precisely models the server-visible state entering the vulnerable endpoint. No stronger impact such as code execution was claimed or tested. ## Root Cause Jenkins's form-authentication path already applied `SessionFixationProtectionStrategy` to `AuthenticationProcessingFilter2`. The separate remember-me auto-login filter did not. In Jenkins 2.579, `SecurityRealm#createFilter` ended the authentication portion of the chain with: ```java filters.add(new RememberMeAuthenticationFilter(sc.manager2, sc.rememberMe2)); ``` When `RememberMeAuthenticationFilter` accepted the victim's persistent token, it associated the resulting authenticated security context with the existing HTTP session but had no `SessionAuthenticationStrategy` to change that session identifier. If the existing session was attacker-known, it therefore became the victim's authenticated session. The 2.580 implementation constructs the filter and explicitly installs Spring Security's fixation protection: ```java final RememberMeAuthenticationFilter rememberMe = new RememberMeAuthenticationFilter(sc.manager2, sc.rememberMe2); rememberMe.setSessionAuthenticationStrategy(new SessionFixationProtectionStrategy()); filters.add(rememberMe); ``` The relevant Jenkins SECURITY-4016 commit is [`69b9bdc1411743f9d0ccf290fb0883659050889a`](https://github.com/jenkinsci/jenkins/commit/69b9bdc1411743f9d0ccf290fb0883659050889a) on the weekly release line (a corresponding backport is `caf1847af29dab83ea58e622f0ec5a9463403d87`). The runtime targets were Jenkins 2.579 source commit `9095ea3a5c5e7dcd392695a5dd880af1c9910ddf` and Jenkins 2.580 source commit `497de4961ad80d97e26bfdeb0d2e40442a84ecb0`. ## Reproduction Steps 1. Run `bundle/repro/reproduction_steps.sh` from any directory. Docker, Git, curl, jq, Python 3, and SHA-256 utilities are required. 2. The script reads `bundle/project_cache_context.json`; when the prepared cache is available it uses `/repo`, otherwise it falls back to `bundle/artifacts/jenkins`. It verifies the exact source commits behind tags `jenkins-2.579` and `jenkins-2.580`. 3. It pulls immutable Jenkins images: - Vulnerable: `jenkins/jenkins@sha256:a7342867ea33efaacf825229d50b7fc77c144ecada9719ab4e32419f5d7412be` - Fixed: `jenkins/jenkins@sha256:0e50a5b11ac14f3b84e529d725ed3a1c4b17ba16188dfa8d9a0189428b0839b1` 4. For two clean attempts per role, it starts a real Jenkins controller on localhost, configures the Jenkins private security realm, and creates the `victim` account. 5. A normal HTTP form login with "remember me" enabled obtains the victim's genuine persistent token. 6. An independent attacker cookie jar visits `/login`, obtaining a server-issued anonymous `JSESSIONID` known to the attacker. 7. The script models cookie planting by combining the attacker session with the victim's persistent token in a new victim-browser jar, then requests `/me/api/json`. This request passes through Jenkins's real `RememberMeAuthenticationFilter`. 8. An independent attacker replay sends only its original session cookie to `/me/api/json`—not the victim credentials or remember-me token. 9. Success requires both 2.579 attempts to preserve the session identifier and return the victim JSON to the attacker, and both 2.580 controls to change the identifier and return HTTP 403 to the attacker. 10. The script redacts cookie values, removes cookie jars/runtime homes, and writes `bundle/repro/runtime_manifest.json` with hashes of finalized proof artifacts. Expected final output: ```text CONFIRMED: Jenkins 2.579 remember-me authentication preserves an attacker-known planted session; Jenkins 2.580 invalidates it. ``` ## Evidence - **Structured verdict:** `bundle/repro/validation_verdict.json` - **Runtime manifest:** `bundle/repro/runtime_manifest.json` - **Final aggregate observations:** `bundle/logs/final_observation_summary.json` - **Fix diff:** `bundle/logs/security4016.patch.txt` - **Primary per-attempt observations:** - `bundle/repro/proof/vulnerable_1.observation.json` - `bundle/repro/proof/vulnerable_2.observation.json` - `bundle/repro/proof/fixed_1.observation.json` - `bundle/repro/proof/fixed_2.observation.json` - **HTTP evidence:** `bundle/repro/proof/*.{login,attacker_session,remember_auth,replay}.headers` and `*.replay.body` - **Controller logs:** `bundle/repro/proof/{vulnerable,fixed}_{1,2}.service.log` - **Immutable target identity:** `bundle/repro/proof/target_identity.txt` Final-run key observations: | Role | Attempt | Planted hash equals post-auth hash | Replay status | Replay is victim | |---|---:|---:|---:|---:| | Jenkins 2.579 vulnerable | 1 | yes | 200 | true | | Jenkins 2.579 vulnerable | 2 | yes | 200 | true | | Jenkins 2.580 fixed | 1 | no | 403 | false | | Jenkins 2.580 fixed | 2 | no | 403 | false | The vulnerable replay body identifies the principal as `victim`, while the fixed replay body states `Authentication required` and `You are authenticated as: anonymous`. Fixed `remember_auth.headers` includes a new `Set-Cookie: JSESSIONID...=`; vulnerable `remember_auth.headers` does not issue a new session cookie. The manifest binds 21 finalized evidence files and was independently rechecked against every SHA-256 value. Environment details: Linux x86-64, rootless Docker, Jenkins 2.579/2.580 official images with bundled Java 21, Winstone/Jetty, and Jenkins's Spring Security remember-me filter. No sanitizer or direct library harness was used. ## Recommendations / Next Steps - Upgrade weekly Jenkins to 2.580 or newer, or LTS Jenkins to 2.568.3 or newer. - Ensure every authentication mechanism, not only password form login, applies the same session-fixation protection at the unauthenticated-to-authenticated transition. - Retain an end-to-end regression test with distinct attacker, victim, planted-browser, and replay clients. Assert both identifier rotation and failure of the old cookie to authorize a protected endpoint. - Reduce cookie-planting opportunities by isolating Jenkins from untrusted sibling subdomains, using secure host/domain boundaries, enforcing HTTPS, and auditing intermediaries for header/cookie injection. - Treat existing remember-me sessions conservatively during upgrade or incident response; invalidate sessions/tokens if cookie planting is suspected. ## Additional Notes - **Idempotency:** Confirmed. The final `reproduction_steps.sh` was run twice consecutively after its last functional/privacy change, and both executions passed all two-vulnerable/two-fixed assertions. - **Evidence hygiene:** Raw session and remember-me cookie values are deleted. Retained HTTP headers replace each `Set-Cookie` value with ``, and observations compare SHA-256 digests rather than exposing identifiers. - **Setup choice:** CSRF is disabled only in each ephemeral test controller so the script can directly exercise the login endpoint without a preliminary crumb exchange. This does not affect the remember-me filter's session-rotation behavior or the replay oracle. - **Limitation:** The proof models the browser result of a same-site planting primitive rather than deploying hostile DNS/subdomains. The actual attacker-controlled cookie crosses the real Jenkins HTTP boundary, and the vulnerable/fixed divergence matches Jenkins's SECURITY-4016 regression test and code fix. ## Reproduction Details Reproduced: 2026-09-11T10:13:41.051Z Duration: 1609 seconds Tool calls: 282 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00351 pruva-verify CVE-2026-84652 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00351&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00351/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-84652 - Source: https://www.jenkins.io/security/advisory/2026-09-02/ ## Artifacts - bundle/repro/rca_report.md (analysis, 9403 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 14535 bytes) - bundle/logs/final_observation_summary.json (other, 1835 bytes) - bundle/logs/security4016.patch.txt (other, 4627 bytes) - bundle/repro/proof/fixed_1.attacker_session.headers (other, 1105 bytes) - bundle/repro/proof/fixed_1.health.txt (other, 60 bytes) - bundle/repro/proof/fixed_1.login.headers (other, 325 bytes) - bundle/repro/proof/fixed_1.observation.json (other, 433 bytes) - bundle/repro/proof/fixed_1.remember_auth.headers (other, 618 bytes) - bundle/repro/proof/fixed_1.replay.body (other, 880 bytes) - bundle/repro/proof/fixed_1.replay.headers (other, 967 bytes) - bundle/repro/proof/fixed_1.service.log (log, 13544 bytes) - bundle/repro/proof/fixed_2.observation.json (other, 433 bytes) - bundle/repro/proof/fixed_2.service.log (log, 13544 bytes) - bundle/repro/proof/target_identity.txt (other, 318 bytes) - bundle/repro/proof/vulnerable_1.attacker_session.headers (other, 1105 bytes) - bundle/repro/proof/vulnerable_1.health.txt (other, 60 bytes) - bundle/repro/proof/vulnerable_1.login.headers (other, 325 bytes) - bundle/repro/proof/vulnerable_1.observation.json (other, 437 bytes) - bundle/repro/proof/vulnerable_1.remember_auth.headers (other, 504 bytes) - bundle/repro/proof/vulnerable_1.replay.body (other, 699 bytes) - bundle/repro/proof/vulnerable_1.replay.headers (other, 504 bytes) - bundle/repro/proof/vulnerable_1.service.log (log, 13542 bytes) - bundle/repro/proof/vulnerable_2.observation.json (other, 437 bytes) - bundle/repro/proof/vulnerable_2.service.log (log, 13641 bytes) - bundle/repro/runtime_manifest.json (other, 4331 bytes) - bundle/repro/validation_verdict.json (other, 1299 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00351 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00351/artifacts/bundle/repro/reproduction_steps.sh - Web: https://www.pruva.dev/reproductions/REPRO-2026-00351 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://www.pruva.dev