Feb 24, 2026·6 min read·1 visit
CVE-2018-1999047 is an improper authorization vulnerability in Jenkins. It allowed any user with 'Overall/Read' access to cancel a pending system restart. While it sounds minor, this flaw creates a 'Denial of Patching' condition, preventing administrators from applying critical updates and maintaining persistence for other exploits.
A logic flaw in Jenkins' UpdateCenter allowed users with minimal read permissions to unilaterally cancel scheduled system restarts. By exploiting the Stapler web framework's auto-routing, attackers could prevent critical security patches from being applied, effectively maintaining a vulnerable state on the server indefinitely.
In the world of DevOps, the 'Safe Restart' is a sacred ritual. You install a plugin, you upgrade the core, and you tell Jenkins to restart when the current build queue is empty. It’s the "set it and forget it" of system administration. You expect that once you pull the trigger, the machine will cycle, patches will apply, and you'll wake up to a fresh, secure environment.
But in late 2018, Jenkins had a ghost in the machine. A vulnerability that turned this administrative mandate into a mere suggestion. Imagine locking the front door, barring the windows, and arming the alarm, only to realize you left a key under the mat labeled "Guest." That is essentially what CVE-2018-1999047 represents.
This wasn't a buffer overflow or a complex memory corruption. It was a failure to ask a simple question: "Who are you?" Deep within the UpdateCenter logic—the brain responsible for keeping your CI/CD pipeline from rotting—Jenkins exposed a cancel button to the world. And by "the world," I mean anyone with a login. Even the intern with read-only access could look at your critical security update schedule and say, "Nah, not today."
To understand this vulnerability, you have to understand Stapler. Stapler is the web framework that powers Jenkins. It’s heavy on convention over configuration, which is developer-speak for "it does magic things you might not expect." Stapler maps URLs directly to Java objects and methods. If you have an object reachable from the root, and that object has a method starting with do... (like doSubmit or doCancel), Stapler automatically turns it into a web endpoint.
This magic is convenient, but it puts the burden of security entirely on the developer. Every do* method effectively becomes a public API endpoint. The developer must manually insert permission checks (e.g., checkPermission(Jenkins.ADMINISTER)) inside the method, or annotate it to restrict access.
In the case of the UpdateCenter class, the method doCancelRestart was exposed. Logically, cancelling a system restart is an administrative action. It affects the availability and integrity of the entire service. However, the code lacked the explicit gatekeeper. Stapler saw the method, created the route [JENKINS_URL]/updateCenter/cancelRestart, and happily served it to anyone who could reach the UpdateCenter object. Since UpdateCenter is generally visible to authenticated users (so they can see plugin statuses), the doCancelRestart method inherited that visibility. It was a wide-open door in a hallway everyone could walk down.
Let's look at the logic. In a secure implementation, any state-changing action—especially one that interrupts the server lifecycle—should verify that the requester holds the keys to the kingdom (specifically Jenkins.ADMINISTER).
The Vulnerable Code (Conceptual):
In the vulnerable versions of UpdateCenter.java, the method looked something like this. Notice the absence of any security assertion:
// Vulnerable: No permission check!
public void doCancelRestart(StaplerResponse rsp) throws IOException, ServletException {
// Logic to cancel the restart task
if (this.coreUpdate != null) {
this.coreUpdate.cancel();
}
// Redirect user back to the dashboard
rsp.sendRedirect2("..");
}The Fix:
The remediation was textbook. The Jenkins team added a single line of code that acts as the bouncer. If the user doesn't have the ADMINISTER bit, the method throws an exception immediately, halting execution before the restart flag is touched.
// Fixed: Explicit permission check added
public void doCancelRestart(StaplerResponse rsp) throws IOException, ServletException {
// The Bouncer: If you aren't admin, you don't get in.
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
if (this.coreUpdate != null) {
this.coreUpdate.cancel();
}
rsp.sendRedirect2("..");
}It is startling how often critical vulnerabilities boil down to a single missing line of boilerplate code. This highlights the fragility of manual access controls versus declarative security frameworks.
Exploiting this vulnerability does not require complex shellcode or heap spraying. It requires a web browser or curl. The barrier to entry is effectively zero, provided you have a low-privileged account on the target instance.
The Scenario:
curl -X POST -u "evil_intern:password" \
http://jenkins.local/updateCenter/cancelRestartThis is a classic Denial of Service regarding administrative intent. It's not crashing the server; it's refusing to let it heal. In a complex attack chain, this capability is gold. It allows an attacker to freeze the environment in a state they have already compromised.
At first glance, a CVSS score of 6.5 seems manageable. "So what? They cancelled a restart." But in security, context is everything. Jenkins is rarely just a build server; it is the production pipeline. It holds credentials for AWS, Docker registries, and signing keys.
Integrity vs. Availability: While NVD rated this as High Integrity impact, I'd argue it's a unique form of Availability impact—specifically, the availability of the update mechanism. If an attacker can veto security patches, they own the timeline.
Consider a scenario where a Zero-Day drops. You rush to patch. You schedule the restart. An adversary inside your network (perhaps a compromised developer account) simply cancels it. You check back hours later—still not patched. You schedule it again. Cancelled again. You are fighting a ghost for control of the server's lifecycle.
Furthermore, this attack is stealthy. Unless you are auditing specific endpoints, the cancellation just looks like a change of plans. "Oh, maybe another admin cancelled it?" It exploits the human element of trust in the system's logs and behavior.
The remediation path is straightforward: Update Jenkins.
The fix was shipped in Jenkins weekly release 2.138 and LTS 2.121.3. If you are running a version older than this, stop reading and go update. Seriously.
Mitigation Strategies:
If you cannot update immediately (why?), you are in a tight spot because this is a core logic flaw. You could try to block access to the /updateCenter/cancelRestart URL pattern via a reverse proxy (Nginx/Apache) or a WAF.
WAF Rule (Conceptual):
# Block access to cancelRestart for non-admin IPs or indiscriminately
location ~ /updateCenter/cancelRestart {
deny all;
return 403;
}However, the only true fix is the code patch that enforces the ADMINISTER permission check. This serves as a reminder that "Internal" dashboards should be treated with the same scrutiny as public-facing APIs.
CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Jenkins (Weekly) Jenkins | <= 2.137 | 2.138 |
Jenkins (LTS) Jenkins | <= 2.121.2 | 2.121.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-863 (Improper Authorization) |
| CVSS v3.0 | 6.5 (Medium) |
| Attack Vector | Network |
| Privileges Required | Low (Overall/Read) |
| Impact | Integrity (High) / Denial of Patching |
| EPSS Score | 0.11% |
The software does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.