Jenkins allows for Privilege Escalation by Remote Authenticated Users
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]
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) { }+}+@Testpublic void testGetDynamic() {
References
- ADVISORYhttps://nvd.nist.gov/vuln/detail/CVE-2015-1814
- WEBhttps://github.com/jenkinsci/jenkins/commit/57e78880cc035874bda916ef4d8d7fd7642af9db
- WEBhttps://access.redhat.com/errata/RHSA-2016:0070
- WEBhttps://bugzilla.redhat.com/show_bug.cgi?id=1205616
- PACKAGEhttps://github.com/jenkinsci/jenkins
- WEBhttps://wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2015-03-23
- WEBhttp://rhn.redhat.com/errata/RHSA-2015-1844.html