Security context
MediumGHSA-3269-jqp5-v8c9 CVE-2015-1814CWE-266Published May 17, 2022

Jenkins allows for Privilege Escalation by Remote Authenticated Users

Research this vulnerability

Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.

Affected versions

1.597 → fixed in 1.6060 → fixed in 1.596.2

Details

The API token-issuing service in Jenkins before 1.606 and LTS before 1.596.2 allows remote attackers to gain privileges via a "forced API token change" involving anonymous users.

The fix

[FIXED SECURITY-180]

Kohsuke Kawaguchi· Mar 7, 2015, 05:35 PM+450c826a1b827
core/src/main/java/jenkins/security/ApiTokenProperty.java+1 0
@@ -81,6 +81,7 @@ public boolean matchesPassword(String password) {
}
public void changeApiToken() throws IOException {
+ user.checkPermission(Jenkins.ADMINISTER);
_changeApiToken();
if (user!=null)
user.save();
test/src/test/java/hudson/model/UserTest.java | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
test/src/test/java/hudson/model/UserTest.java+44 0
@@ -28,19 +28,27 @@
import com.gargoylesoftware.htmlunit.WebAssert;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
+
import hudson.security.AccessDeniedException2;
import hudson.security.GlobalMatrixAuthorizationStrategy;
import hudson.security.HudsonPrivateSecurityRealm;
import hudson.security.Permission;
import hudson.tasks.MailAddressResolver;
+
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collections;
+
import jenkins.model.Jenkins;
+import jenkins.security.ApiTokenProperty;
+
+import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
+
import static org.junit.Assert.*;
+
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Bug;
@@ -419,6 +427,42 @@ public void testCanDelete() throws IOException {
assertTrue("User should be able to delete.", user.canDelete());
}
+ @Test
+ // @Issue("SECURITY-180")
+ public void security180() throws Exception {
+ final GlobalMatrixAuthorizationStrategy auth = new GlobalMatrixAuthorizationStrategy();
+ j.jenkins.setAuthorizationStrategy(auth);
+ j.jenkins.setSecurityRealm(new HudsonPrivateSecurityRealm(false));
+
+ User alice = User.get("alice");
+ User bob = User.get("bob");
+ User anonymous = User.get("anonymous");
+ User admin = User.get("admin");
+
+ auth.add(Jenkins.READ, alice.getId());
+ auth.add(Jenkins.READ, bob.getId());
+ auth.add(Jenkins.ADMINISTER, admin.getId());
+
+ SecurityContextHolder.getContext().setAuthentication(admin.impersonate());
+ // Change token by admin
+ admin.getProperty(ApiTokenProperty.class).changeApiToken();
+ alice.getProperty(ApiTokenProperty.class).changeApiToken();
+
+ SecurityContextHolder.getContext().setAuthentication(bob.impersonate());
+ // Change own token
+ bob.getProperty(ApiTokenProperty.class).changeApiToken();
+
+ try {
+ alice.getProperty(ApiTokenProperty.class).changeApiToken();
+ fail("Bob should not be authorized to change alice's token");
+ } catch (AccessDeniedException expected) { }
+
+ try {
+ anonymous.getProperty(ApiTokenProperty.class).changeApiToken();
+ fail("Anonymous should not be authorized to change alice's token");
+ } catch (AccessDeniedException expected) { }
+ }
+
@Test
public void testGetDynamic() {

References