Someone left the castle's back gate unbolted because the guard only checks IDs at the front door, not the service entrance marked 'resolves-to'
CVE-2026-117612 (SECURITY-4125) is a deserialization filter bypass in Jenkins core. Jenkins uses XStream to serialize and deserialize configuration data — job configs, views, agent definitions — as XML. Since JEP-200, Jenkins maintains a strict allowlist of types that may be deserialized. However, Jenkins' custom XStream converter failed to apply this filter to classes referenced by the resolves-to XML attribute. An attacker who can POST a crafted config.xml (requiring Overall/Read plus Item/Configure, View/Configure, or Agent/Configure permission) can reference an arbitrary Java class via resolves-to, bypass the entire JEP-200 allowlist, instantiate a dangerous gadget class, and achieve remote code execution on the Jenkins controller. Affected versions: Jenkins weekly ≤2.587 and LTS ≤2.580.1. Fixed in weekly 2.588 and LTS 2.580.2, disclosed 2026-10-28.
The Jenkins project rates this High, and the CVSS vector they published (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 8.8) is a fair technical description of the auth-required, network-reachable, full-CIA-impact chain. However, CVSS does not account for *what* the target is. Jenkins controllers are the nerve center of CI/CD pipelines: they hold deployment credentials, signing keys, source-code access tokens, and orchestrate builds that ship to production. RCE on a Jenkins controller is not an 8.8-level event — it is a supply-chain-scale compromise. The vendor severity underweights the blast radius by treating Jenkins like a generic web application rather than the crown-jewel CI/CD infrastructure it is.
5 steps from start to impact.
Authenticate to Jenkins
- Valid Jenkins account with Overall/Read permission
- Network access to Jenkins controller (port 8080 or reverse-proxied)
- Jenkins is rarely internet-facing in mature enterprises — typically behind VPN or corporate network
- Account requires Overall/Read, which is the minimum useful permission but still requires authentication
Identify a writable config endpoint
POSTing XML configuration. Item/Configure (the most common), View/Configure, or Agent/Configure all qualify. In most Jenkins deployments, developers and pipeline engineers hold Item/Configure on at least some jobs. The attacker identifies a job, view, or agent they can configure and targets its config.xml REST endpoint.- One of: Item/Configure, View/Configure, or Agent/Configure on at least one object
- Least-privilege Jenkins deployments restrict Item/Configure to pipeline admins only
- Matrix Authorization or Role Strategy plugins can limit who configures what
Craft malicious XML with resolves-to gadget
resolves-to attribute referencing a dangerous class on Jenkins' classpath — such as Groovy's MethodClosure, a Spring gadget, or a Commons Collections transformer chain. Because the custom converter does not apply the deserialization filter to the resolves-to target class, the gadget is instantiated unchecked. The technique mirrors historical XStream CVEs (CVE-2021-39148, CVE-2021-39145) where type-substitution attributes were used to smuggle disallowed types past allowlists.- Knowledge of available gadget classes on Jenkins' classpath
- Ability to craft XStream XML with resolves-to attribute
- No public PoC exists as of 2026-10-29, raising the bar for unsophisticated attackers
- Available gadget classes depend on installed plugins — a minimal Jenkins install has fewer gadgets than a plugin-heavy one
resolves-to attributes containing known gadget class names; Snort/Suricata signatures for XStream exploitation patternsPOST payload to config.xml endpoint
POST /job/<name>/config.xml (or the equivalent view/agent endpoint) with their authenticated session. Jenkins accepts the XML and passes it to XStream for deserialization. The converter processes the resolves-to attribute without filtering, instantiating the attacker's chosen class.- Authenticated session from Step 1
- Config permission from Step 2
- Crafted payload from Step 3
- Content-Security-Policy headers and CRUMB tokens must be handled — standard for Jenkins API interaction but adds minor complexity
- Some WAFs may block suspicious XML payloads
Achieve RCE on Jenkins controller → supply-chain pivot
jenkins on Linux or SYSTEM on Windows). The attacker now has full control of the CI/CD controller: access to all stored credentials (via /credentials/), ability to modify any pipeline, inject malicious build steps, exfiltrate source code, poison build artifacts, or pivot laterally to deployment targets. On Windows controllers running as SYSTEM, this also means local privilege escalation. The attacker can persist by installing a malicious plugin or modifying init scripts.- Successful deserialization of gadget chain
- Jenkins process user has access to credential store and pipeline definitions
- Credential rotation and secrets management (e.g., HashiCorp Vault integration) limits the shelf-life of stolen secrets
- Pipeline-as-code (Jenkinsfile in SCM) makes artifact poisoning more detectable via git diffs
resolves-to classes and add XStream type permission checks. Per the noisgate mitigation SLA for CRITICAL, deploy within 3 days. Test in staging first — the new type permission checks may reject legitimate plugin serialization patterns. If breakage occurs, the system property hudson.util.XStream2.SUPPRESS_TYPE_PERMISSION=true can temporarily disable the new checks, but this re-opens the vulnerability and must be a time-boxed exception./*/config.xml endpoints and block requests containing resolves-to attributes referencing classes outside a known-good allowlist. This is a defense-in-depth measure — it can be bypassed with encoding tricks but raises the bar significantly. Use ModSecurity, AWS WAF, or Cloudflare custom rules.- Setting
SUPPRESS_TYPE_PERMISSION=true— this system property *disables* the fix introduced in 2.588/2.580.2. It exists for compatibility, not security. Setting it re-opens CVE-2026-117612 entirely. - Jenkins Script Security plugin — Script Security governs Groovy sandbox execution in pipelines, not XStream deserialization. The vulnerable code path runs before Script Security is invoked.
- Disabling anonymous read access — the vulnerability already requires authentication. If you didn't have anonymous access before, this changes nothing. If you did, disabling it doesn't help because the attacker needs Item/Configure, which anonymous users never have.
- Upgrading XStream library alone — this is a Jenkins converter bug, not an upstream XStream bug. The XStream library itself passes the
resolves-toclass to Jenkins' custom converter, which fails to filter it. Only the Jenkins core fix addresses this.
The supporting signals.
| In-the-wild exploitation | No confirmed exploitation. Not listed in CISA KEV. SecurityOnline reports status as 'Not exploited' as of 2026-10-28. This is a 1-day-old disclosure; exploitation status should be monitored closely given Jenkins' history as a high-value target. |
|---|---|
| Proof-of-concept | No public PoC found. No repositories on GitHub, no entries on pocindex.io or ExploitDB. However, the technique is well-understood: prior XStream type-substitution bypasses (CVE-2021-39148, CVE-2021-39145, CVE-2020-26217) have published PoCs and Metasploit modules (exploit/multi/http/jenkins_xstream_deserialize). Weaponization is likely a matter of days to weeks for skilled researchers. |
| EPSS | Not yet scored — CVE was published 2026-10-28 and EPSS has not ingested it. For reference, the prior Jenkins XStream RCE CVE-2026-84645 (SECURITY-3972, September 2026) carries an EPSS of 0.66 (66th percentile) per Bitsight, indicating high predicted exploitation probability for this class of bug. |
| KEV status | Not listed in CISA Known Exploited Vulnerabilities catalog as of 2026-10-29. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 8.8 (High) per the Jenkins advisory. Network-reachable, low complexity, low privilege required (authenticated), no user interaction. Full CIA impact. The Scope:Unchanged is accurate — exploitation stays on the Jenkins controller, though the *business* blast radius extends to the entire software supply chain. |
| Affected versions | Jenkins weekly ≤2.587; LTS ≤2.580.1. VulDB incorrectly lists the range as up to 2.579.x — use the Jenkins advisory as authoritative. |
| Fixed versions | Jenkins weekly 2.588; LTS 2.580.2. Both released 2026-10-28. No distro backports identified yet — Debian, Red Hat, and FreeBSD packages should be monitored for updates. Docker image jenkins/jenkins:2.588 and jenkins/jenkins:2.580.2-lts are the targets. |
| Exposure data | Shadowserver identified ~45,000 internet-facing Jenkins instances globally in January 2024 (during CVE-2024-23897). Current numbers are likely similar. Most are in the US (~11,800) and China (~12,000). However, the majority of enterprise Jenkins instances are internal — Shodan/Censys only see a fraction of the installed base. The auth requirement makes internet exposure less relevant than internal exposure. |
| Disclosure timeline | Reserved 2026-10-27, published 2026-10-28 in Jenkins Security Advisory 2026-10-28. SECURITY-4125. Part of a 15-CVE advisory batch. |
| Researcher / reporter | Not publicly credited in available sources. The Jenkins advisory (SECURITY-4125) does not name the reporter in the materials retrieved. |
Sources.
- Jenkins Security Advisory 2026-10-28 (SECURITY-4125)
- CVE-2026-117612 — ThreatInt CVE Database
- CVE-2026-117612 — Strix AI
- Jenkins Fixes 15 Vulnerabilities — SecurityOnline
- Bitsight CVE-2026-84645 (prior Jenkins XStream RCE, EPSS reference)
- Shadowserver — 45K Jenkins Instances Exposed (exposure baseline)
- Metasploit Jenkins XStream Deserialize Module (historical technique reference)
- Red Hat — Java Deserialization Flaws Part 2: XML Deserialization (XStream technique background)
Why this verdict
- Filter bypass yields full RCE: The
resolves-toattribute bypass completely defeats JEP-200, Jenkins' primary deserialization defense. Once bypassed, the attacker has the same primitive as pre-JEP-200 Jenkins: arbitrary class instantiation during deserialization, which reliably chains to code execution via well-known gadgets (Groovy, Commons Collections, Spring) present on Jenkins' classpath. - Authentication is real friction but not high friction: The chain requires Overall/Read + Item/Configure. In practice, most Jenkins environments grant Item/Configure to dozens or hundreds of developers and pipeline engineers. A single compromised developer laptop, leaked API token, or malicious insider satisfies this prerequisite. This is PR:L, not PR:H — and the population of users who hold this permission is large relative to the organization.
- No user interaction, low complexity: The attack is a single authenticated API call —
POST /job/<name>/config.xmlwith crafted XML. No race conditions, no memory corruption, no multi-step social engineering. Once you have the creds and the payload, it's deterministic. - Role multiplier: Jenkins is *canonically* a CI/CD controller — 100% of its installed base serves this role. RCE on the Jenkins controller delivers: (1) access to all stored credentials (deployment keys, cloud API tokens, registry passwords, signing certificates), (2) ability to inject malicious code into any build pipeline, (3) supply-chain compromise of every artifact built by that instance, (4) lateral movement to production environments via deployment pipelines. The blast radius is supply-chain-scale. Per the deployment-role blast radius rule, because Jenkins is canonically a high-value CI/CD component and the chain succeeds with supply-chain-pivot outcome, the verdict floor is CRITICAL.
- Historical pattern: Jenkins XStream deserialization bugs are reliably weaponized. CVE-2016-0792 had a Metasploit module. CVE-2026-53435 (same bug class, June 2026) is reported as actively exploited. CVE-2026-84645 (September 2026) has an EPSS of 0.66. The absence of a PoC today does not mean absence next week — the technique is public and well-documented.
Why not higher?
A 9.1 already reflects the supply-chain blast radius. It is not 9.8–10.0 because the chain requires authenticated access with a specific permission set (PR:L), which meaningfully narrows the attacker population compared to a pre-auth RCE. If this were exploitable by anonymous users or unauthenticated attackers, it would be 9.8+.
Why not lower?
Downgrading below CRITICAL would require evidence that fewer than 10% of Jenkins installations serve a CI/CD role — which is definitionally impossible since Jenkins *is* a CI/CD tool. The authentication requirement is real friction, but friction cannot pull below the floor set by the blast radius. Every Jenkins controller holds credentials, orchestrates builds, and touches production deployments. An 8.8 (High) rating treats this like RCE on a blog server; it is RCE on the software factory.
Crowdsourced verification payload.
Run this script from any workstation that has curl and network access to the Jenkins controller. Pass the Jenkins URL as the first argument. No authentication is required — the script reads the X-Jenkins response header, which is returned on unauthenticated requests to the login page. Example: ./check_cve_2026_117612.sh https://jenkins.example.com:8080
#!/usr/bin/env bash
# CVE-2026-117612 checker — Jenkins XStream resolves-to deserialization filter bypass
# Usage: ./check_cve_2026_117612.sh <JENKINS_URL>
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -uo pipefail
JENKINS_URL="${1:?Usage: $0 <JENKINS_URL>}"
JENKINS_URL="${JENKINS_URL%/}"
# Retrieve Jenkins version from X-Jenkins header
VERSION=$(curl -sI --max-time 10 "${JENKINS_URL}/login" 2>/dev/null \
| grep -i '^X-Jenkins:' \
| awk '{print $2}' \
| tr -d '\r\n ')
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN — could not retrieve Jenkins version from ${JENKINS_URL}"
echo "Verify the URL is correct and the server is reachable."
exit 2
fi
echo "Detected Jenkins version: ${VERSION}"
# Semantic version comparison: returns 0 if $1 >= $2
version_ge() {
printf '%s\n%s' "$2" "$1" | sort -V -C
}
# Determine release line by segment count
IFS='.' read -ra PARTS <<< "$VERSION"
if [[ ${#PARTS[@]} -ge 3 ]]; then
# LTS release (e.g., 2.580.2) — fixed in 2.580.2
FIXED="2.580.2"
if version_ge "$VERSION" "$FIXED"; then
echo "PATCHED — LTS ${VERSION} >= ${FIXED}"
exit 0
else
echo "VULNERABLE — LTS ${VERSION} < ${FIXED} (CVE-2026-117612)"
echo "Action: Upgrade to Jenkins LTS ${FIXED} or later."
exit 1
fi
else
# Weekly release (e.g., 2.588) — fixed in 2.588
FIXED="2.588"
if version_ge "$VERSION" "$FIXED"; then
echo "PATCHED — Weekly ${VERSION} >= ${FIXED}"
exit 0
else
echo "VULNERABLE — Weekly ${VERSION} < ${FIXED} (CVE-2026-117612)"
echo "Action: Upgrade to Jenkins Weekly ${FIXED} or later."
exit 1
fi
fi