## 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 `<project_cache_dir>/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...=<redacted>`; 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 `<redacted>`, 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.
