Security context
Medium· 4.3GHSA-565r-pf5q-45v6 CVE-2025-31720CWE-862Published Apr 2, 2025

Jenkins Missing Permission Check

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

2.500 → fixed in 2.5040 → fixed in 2.492.3

Details

Jenkins 2.503 and earlier, LTS 2.492.2 and earlier does not perform a permission check in an HTTP endpoint. This allows attackers with Computer/Create permission but without Computer/Extended Read permission to copy an agent, gaining access to its configuration. Jenkins 2.504, LTS 2.492.3 requires Computer/Extended Read permission to copy an agent.

The fix

[SECURITY-3512]

Daniel Beck· Mar 25, 2025, 09:02 PM+640bf32018bf0
core/src/main/java/hudson/model/ComputerSet.java+2 0
@@ -291,6 +291,8 @@ public synchronized void doCreateItem(StaplerRequest2 req, StaplerResponse2 rsp,
}
}
+ src.checkPermission(Computer.EXTENDED_READ);
+
// copy through XStream
String xml = Jenkins.XSTREAM.toXML(src);
Node result = (Node) Jenkins.XSTREAM.fromXML(xml);
test/src/test/java/jenkins/security/Security3512Test.java+62 0
@@ -0,0 +1,62 @@
+package jenkins.security;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+
+import hudson.model.Computer;
+import hudson.slaves.DumbSlave;
+import java.net.URL;
+import jenkins.model.Jenkins;
+import org.htmlunit.HttpMethod;
+import org.htmlunit.WebRequest;
+import org.htmlunit.WebResponse;
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.Issue;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.MockAuthorizationStrategy;
+
+public class Security3512Test {
+
+ @Rule
+ public JenkinsRule j = new JenkinsRule();
+
+ @Test
+ @Issue("SECURITY-3512")
+ public void copyAgentTest() throws Exception {
+ Computer.EXTENDED_READ.setEnabled(true);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ MockAuthorizationStrategy mockAuthorizationStrategy = new MockAuthorizationStrategy();
+ mockAuthorizationStrategy.grant(Jenkins.READ, Computer.CREATE, Computer.EXTENDED_READ).everywhere().to("alice");
+ mockAuthorizationStrategy.grant(Jenkins.READ, Computer.CREATE).everywhere().to("bob");
+ j.jenkins.setAuthorizationStrategy(mockAuthorizationStrategy);
+
+ DumbSlave agent = j.createOnlineSlave();
+
+ assertEquals(2, j.getInstance().getComputers().length);
+
+ String agentCopyURL = j.getURL() + "/computer/createItem?mode=copy&from=" + agent.getNodeName() + "&name=";
+
+ { // with ExtendedRead permission you can copy a node
+ try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false).login("alice")) {
+ WebResponse rsp = wc.getPage(wc.addCrumb(new WebRequest(new URL(agentCopyURL + "aliceAgent"),
+ HttpMethod.POST))).getWebResponse();
+
+ assertEquals(200, rsp.getStatusCode());
+ assertEquals(3, j.getInstance().getComputers().length);
+ }
+ }
+
+ { // without ExtendedRead permission you cannot copy a node
+ try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false).login("bob")) {
+ WebResponse rsp = wc.getPage(wc.addCrumb(new WebRequest(new URL(agentCopyURL + "bobAgent"),
+ HttpMethod.POST))).getWebResponse();
+
+ assertEquals(403, rsp.getStatusCode());
+ assertThat(rsp.getContentAsString(), containsString("bob is missing the Agent/ExtendedRead permission"));
+ assertEquals(3, j.getInstance().getComputers().length);
+ }
+ }
+ }
+}

References