Jenkins Missing Permission Check
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
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/Configure permission to copy an agent, gaining access to encrypted secrets in its configuration. This is due to an incomplete fix of [SECURITY-3495](https://www.jenkins.io/security/advisory/2025-03-05/#SECURITY-3495)/CVE-2025-27622. Jenkins 2.504, LTS 2.492.3 requires Computer/Configure permission to copy an agent containing secrets.
The fix
[SECURITY-3513]
core/src/main/java/hudson/model/ComputerSet.java+15 −0
@@ -61,6 +61,7 @@import jenkins.model.Jenkins;import jenkins.model.ModelObjectWithChildren;import jenkins.model.ModelObjectWithContextMenu.ContextMenu;+import jenkins.security.ExtendedReadRedaction;import jenkins.util.Timer;import jenkins.widgets.HasWidgets;import net.sf.json.JSONObject;@@ -75,6 +76,7 @@import org.kohsuke.stapler.export.ExportedBean;import org.kohsuke.stapler.interceptor.RequirePOST;import org.kohsuke.stapler.verb.POST;+import org.springframework.security.access.AccessDeniedException;/*** Serves as the top of {@link Computer}s in the URL hierarchy.@@ -295,6 +297,19 @@ public synchronized void doCreateItem(StaplerRequest2 req, StaplerResponse2 rsp,// copy through XStreamString xml = Jenkins.XSTREAM.toXML(src);+if (!src.hasPermission(Computer.CONFIGURE)) {+final String redactedConfigDotXml = ExtendedReadRedaction.applyAll(xml);+if (!xml.equals(redactedConfigDotXml)) {+// AccessDeniedException2 does not permit a custom message, and anyway redirecting the user to the login screen is obviously pointless.+throw new AccessDeniedException(+Messages.ComputerSet_may_not_copy_as_it_contains_secrets_and_(+src.getNodeName(),+Jenkins.getAuthentication2().getName(),+Computer.PERMISSIONS.title,+Computer.EXTENDED_READ.name,+Computer.CONFIGURE.name));+}+}Node result = (Node) Jenkins.XSTREAM.fromXML(xml);result.setNodeName(name);result.holdOffLaunchUntilSave = true;
core/src/main/resources/hudson/model/Messages.properties+1 −0
@@ -112,6 +112,7 @@ ComputerSet.NoSuchSlave=No such agent: {0}ComputerSet.SlaveAlreadyExists=Agent called ‘{0}’ already existsComputerSet.SpecifySlaveToCopy=Specify which agent to copyComputerSet.DisplayName=Nodes+ComputerSet.may_not_copy_as_it_contains_secrets_and_=May not copy {0} as it contains secrets and {1} has {2}/{3} but not /{4}Descriptor.From=(from <a href="{1}" rel="noopener noreferrer" target="_blank">{0}</a>)
core/src/main/resources/hudson/model/Messages_bg.properties+5 −0
@@ -155,6 +155,11 @@ CLI.wait-node-offline.shortDescription=\ComputerSet.DisplayName=\Компютри+# May not copy {0} as it contains secrets and {1} has {2}/{3} but not /{4}+ComputerSet.may_not_copy_as_it_contains_secrets_and_=\+Не може да копирате „{0}“, защото на него има пароли, а „{1}“ има {2}/{3}, но\+не и /{4}+Descriptor.From=\(от <a href="{1}">{0}</a>)
core/src/main/resources/hudson/model/Messages_de.properties+1 −0
@@ -107,6 +107,7 @@ ComputerSet.DisplayName=KnotenComputerSet.NoSuchSlave=Agent existiert nicht: „{0}“ComputerSet.SlaveAlreadyExists=Ein Agent mit dem Namen „{0}“ existiert bereits.ComputerSet.SpecifySlaveToCopy=Geben Sie an, welcher Agent kopiert werden soll+ComputerSet.may_not_copy_as_it_contains_secrets_and_=Kann „{0}“ nicht kopieren, da es Geheimnisse enthält und „{1}“ nur {2}/{3} aber nicht /{4} hat.Descriptor.From=(aus <a href="{1}">{0}</a>)
core/src/main/resources/hudson/model/Messages_fr.properties+1 −0
@@ -107,6 +107,7 @@ ComputerSet.NoSuchSlave=Aucun agent: {0}ComputerSet.SlaveAlreadyExists=L''agent ‘{0}’ existe déjàComputerSet.SpecifySlaveToCopy=Quel agent à copierComputerSet.DisplayName=Nœuds+ComputerSet.may_not_copy_as_it_contains_secrets_and_=Copie impossible de {0} car il contient des secrets et {1} possède {2}/{3} mais pas /{4}Descriptor.From=(de <a href="{1}" rel="noopener noreferrer" target="_blank">{0}</a>)
core/src/main/resources/hudson/model/Messages_it.properties+2 −0
@@ -156,6 +156,8 @@ ComputerSet.DisplayName=NodiComputerSet.NoSuchSlave=L''agente {0} non esisteComputerSet.SlaveAlreadyExists=Un agente di nome "{0}" esiste giàComputerSet.SpecifySlaveToCopy=Specificare l''agente da copiare+ComputerSet.may_not_copy_as_it_contains_secrets_and_=Impossibile copiare \+{0} perché contiene segreti e {1} ha {2}/{3} ma non /{4}Descriptor.From=(da <a href="{1}">{0}</a>)Executor.NotAvailable=N/DFileParameterDefinition.DisplayName=Parametro file
core/src/main/resources/hudson/model/Messages_pt_BR.properties+2 −0
@@ -105,6 +105,8 @@ UpdateCenter.Status.Success=SucessoUpdateCenter.init=Iniciando o centro de atualizaçãoParameterAction.DisplayName=ParâmetrosComputerSet.DisplayName=Nós+ComputerSet.may_not_copy_as_it_contains_secrets_and_=Pode não conseguir copiar {0} já que contem segredos e {1} \+tem {2}/{3} mas não /{4}Run.DeletePermission.Description=\Esta permissão concede aos usuários o direito de manualmente apagarem construções do histórico de construções.Run.Summary.BrokenForALongTime=Quebrado há muito tempo
core/src/main/resources/hudson/model/Messages_sr.properties+1 −0
@@ -85,6 +85,7 @@ Computer.BadChannel=Машина агента није повезана илиComputerSet.SlaveAlreadyExists=Већ постоји агент са именом {0}ComputerSet.SpecifySlaveToCopy=Одредите агент за копирањеComputerSet.DisplayName=Рачунари+ComputerSet.may_not_copy_as_it_contains_secrets_and_=Неможе се копирати {0} пошто садржи тајне и {1} има {2}/{3} а не /{4}Descriptor.From=(од <a href="{1}">{0}</a>)Executor.NotAvailable=Н/ДFreeStyleProject.DisplayName=Независни задатак