Critical severity9.8NVD Advisory· Published Jan 12, 2017· Updated May 13, 2026
CVE-2016-9299
CVE-2016-9299
Description
The remoting module in Jenkins before 2.32 and LTS before 2.19.3 allows remote attackers to execute arbitrary code via a crafted serialized Java object, which triggers an LDAP query to a third-party server.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.jenkins-ci.main:jenkins-coreMaven | >= 2.20, < 2.32 | 2.32 |
org.jenkins-ci.main:jenkins-coreMaven | < 2.19.3 | 2.19.3 |
Patches
5ce8a2d51a5eeMerge pull request #73 from jenkinsci-cert/20161116-for-1.625
4 files changed · +38 −4
pom.xml+1 −1 modified@@ -179,7 +179,7 @@ THE SOFTWARE. <dependency> <groupId>org.jenkins-ci.main</groupId> <artifactId>remoting</artifactId> - <version>2.53.3</version> + <version>2.53.4</version> </dependency> <dependency>
test/src/test/java/jenkins/security/Security218CliTest.java+9 −0 modified@@ -161,6 +161,15 @@ public void probeSpring2() throws Exception { probe(Payload.Spring2, -1); } + @PresetData(PresetData.DataSet.ANONYMOUS_READONLY) + @Test + @Issue("SECURITY-360") + public void ldap() throws Exception { + // with a proper fix, this should fail with EXIT_CODE_REJECTED + // otherwise this will fail with -1 exit code + probe(Payload.Ldap, PayloadCaller.EXIT_CODE_REJECTED); + } + private void probe(Payload payload, int expectedResultCode) throws Exception { File file = File.createTempFile("security-218", payload + "-payload"); File moved = new File(file.getAbsolutePath() + "-moved");
test/src/test/java/jenkins/security/security218/Payload.java+4 −3 modified@@ -24,7 +24,6 @@ package jenkins.security.security218; import jenkins.security.security218.ysoserial.payloads.*; -import net.sf.json.JSON; /** @@ -46,8 +45,10 @@ public enum Payload { JRMPListener(JRMPListener.class), JSON1(JSON1.class), Spring1(Spring1.class), - Spring2(Spring2.class); - + Spring2(Spring2.class), + Ldap(Ldap.class), + ; + private final Class<? extends ObjectPayload> payloadClass; private Payload(Class<? extends ObjectPayload> payloadClass) {
test/src/test/java/jenkins/security/security218/ysoserial/payloads/Ldap.java+24 −0 added@@ -0,0 +1,24 @@ +package jenkins.security.security218.ysoserial.payloads; + +import jenkins.security.security218.ysoserial.util.PayloadRunner; + +import java.lang.reflect.Constructor; + +/** + * @author Kohsuke Kawaguchi + */ +public class Ldap extends PayloadRunner implements ObjectPayload<Object> { + + public Object getObject(final String command) throws Exception { + // this is not a fully exploit, so we cannot honor the command, + // but we want to check that we are blocking LdapAttribute + Class<?> c = Class.forName("com.sun.jndi.ldap.LdapAttribute"); + Constructor<?> ctr = c.getDeclaredConstructor(String.class); + ctr.setAccessible(true); + return ctr.newInstance("foo"); + } + + public static void main(final String[] args) throws Exception { + PayloadRunner.run(Ldap.class, args); + } +}
d84d9a2ad382Preparing the integration commit for Nov 16th release
4 files changed · +38 −4
pom.xml+1 −1 modified@@ -179,7 +179,7 @@ THE SOFTWARE. <dependency> <groupId>org.jenkins-ci.main</groupId> <artifactId>remoting</artifactId> - <version>2.53.3</version> + <version>2.53.4-20161112.024902-1</version> </dependency> <dependency>
test/src/test/java/jenkins/security/Security218CliTest.java+9 −0 modified@@ -161,6 +161,15 @@ public void probeSpring2() throws Exception { probe(Payload.Spring2, -1); } + @PresetData(PresetData.DataSet.ANONYMOUS_READONLY) + @Test + @Issue("SECURITY-360") + public void ldap() throws Exception { + // with a proper fix, this should fail with EXIT_CODE_REJECTED + // otherwise this will fail with -1 exit code + probe(Payload.Ldap, PayloadCaller.EXIT_CODE_REJECTED); + } + private void probe(Payload payload, int expectedResultCode) throws Exception { File file = File.createTempFile("security-218", payload + "-payload"); File moved = new File(file.getAbsolutePath() + "-moved");
test/src/test/java/jenkins/security/security218/Payload.java+4 −3 modified@@ -24,7 +24,6 @@ package jenkins.security.security218; import jenkins.security.security218.ysoserial.payloads.*; -import net.sf.json.JSON; /** @@ -46,8 +45,10 @@ public enum Payload { JRMPListener(JRMPListener.class), JSON1(JSON1.class), Spring1(Spring1.class), - Spring2(Spring2.class); - + Spring2(Spring2.class), + Ldap(Ldap.class), + ; + private final Class<? extends ObjectPayload> payloadClass; private Payload(Class<? extends ObjectPayload> payloadClass) {
test/src/test/java/jenkins/security/security218/ysoserial/payloads/Ldap.java+24 −0 added@@ -0,0 +1,24 @@ +package jenkins.security.security218.ysoserial.payloads; + +import jenkins.security.security218.ysoserial.util.PayloadRunner; + +import java.lang.reflect.Constructor; + +/** + * @author Kohsuke Kawaguchi + */ +public class Ldap extends PayloadRunner implements ObjectPayload<Object> { + + public Object getObject(final String command) throws Exception { + // this is not a fully exploit, so we cannot honor the command, + // but we want to check that we are blocking LdapAttribute + Class<?> c = Class.forName("com.sun.jndi.ldap.LdapAttribute"); + Constructor<?> ctr = c.getDeclaredConstructor(String.class); + ctr.setAccessible(true); + return ctr.newInstance("foo"); + } + + public static void main(final String[] args) throws Exception { + PayloadRunner.run(Ldap.class, args); + } +}
6078dd7aa097[SECURITY-360] integration test
4 files changed · +38 −4
pom.xml+1 −1 modified@@ -179,7 +179,7 @@ THE SOFTWARE. <dependency> <groupId>org.jenkins-ci.main</groupId> <artifactId>remoting</artifactId> - <version>2.53.3-20160211.162333-3</version> + <version>2.53.2</version> </dependency> <dependency>
test/src/test/java/jenkins/security/Security218CliTest.java+9 −0 modified@@ -161,6 +161,15 @@ public void probeSpring2() throws Exception { probe(Payload.Spring2, -1); } + @PresetData(PresetData.DataSet.ANONYMOUS_READONLY) + @Test + @Issue("SECURITY-360") + public void ldap() throws Exception { + // with a proper fix, this should fail with EXIT_CODE_REJECTED + // otherwise this will fail with -1 exit code + probe(Payload.Ldap, PayloadCaller.EXIT_CODE_REJECTED); + } + private void probe(Payload payload, int expectedResultCode) throws Exception { File file = File.createTempFile("security-218", payload + "-payload"); File moved = new File(file.getAbsolutePath() + "-moved");
test/src/test/java/jenkins/security/security218/Payload.java+4 −3 modified@@ -24,7 +24,6 @@ package jenkins.security.security218; import jenkins.security.security218.ysoserial.payloads.*; -import net.sf.json.JSON; /** @@ -46,8 +45,10 @@ public enum Payload { JRMPListener(JRMPListener.class), JSON1(JSON1.class), Spring1(Spring1.class), - Spring2(Spring2.class); - + Spring2(Spring2.class), + Ldap(Ldap.class), + ; + private final Class<? extends ObjectPayload> payloadClass; private Payload(Class<? extends ObjectPayload> payloadClass) {
test/src/test/java/jenkins/security/security218/ysoserial/payloads/Ldap.java+24 −0 added@@ -0,0 +1,24 @@ +package jenkins.security.security218.ysoserial.payloads; + +import jenkins.security.security218.ysoserial.util.PayloadRunner; + +import java.lang.reflect.Constructor; + +/** + * @author Kohsuke Kawaguchi + */ +public class Ldap extends PayloadRunner implements ObjectPayload<Object> { + + public Object getObject(final String command) throws Exception { + // this is not a fully exploit, so we cannot honor the command, + // but we want to check that we are blocking LdapAttribute + Class<?> c = Class.forName("com.sun.jndi.ldap.LdapAttribute"); + Constructor<?> ctr = c.getDeclaredConstructor(String.class); + ctr.setAccessible(true); + return ctr.newInstance("foo"); + } + + public static void main(final String[] args) throws Exception { + PayloadRunner.run(Ldap.class, args); + } +}
f574224cae5fMerge pull request #71 from jenkinsci-cert/SECURITY-360
6 files changed · +86 −5
core/src/main/java/hudson/cli/CLIAction.java+1 −2 modified@@ -62,12 +62,11 @@ public String getIconFileName() { } public String getDisplayName() { - return "Jenkins CLI"; } public String getUrlName() { - return "cli"; + return jenkins.CLI.DISABLED ? null : "cli"; } public void doCommand(StaplerRequest req, StaplerResponse rsp) throws ServletException, IOException {
core/src/main/java/hudson/cli/CliProtocol2.java+1 −1 modified@@ -24,7 +24,7 @@ public class CliProtocol2 extends CliProtocol { @Override public String getName() { - return "CLI2-connect"; + return jenkins.CLI.DISABLED ? null : "CLI2-connect"; } @Override
core/src/main/java/hudson/cli/CliProtocol.java+1 −1 modified@@ -32,7 +32,7 @@ public class CliProtocol extends AgentProtocol { @Override public String getName() { - return "CLI-connect"; + return jenkins.CLI.DISABLED ? null : "CLI-connect"; } @Override
core/src/main/java/jenkins/CLI.java+17 −0 added@@ -0,0 +1,17 @@ +package jenkins; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +/** + * Kill switch to disable the entire Jenkins CLI system. + * + * Marked as no external use because the CLI subsystem is nearing EOL. + * + * @author Kohsuke Kawaguchi + */ +@Restricted(NoExternalUse.class) +public class CLI { + // non-final to allow setting from $JENKINS_HOME/init.groovy.d + public static boolean DISABLED = Boolean.getBoolean(CLI.class.getName()+".disabled"); +}
pom.xml+1 −1 modified@@ -179,7 +179,7 @@ THE SOFTWARE. <dependency> <groupId>org.jenkins-ci.main</groupId> <artifactId>remoting</artifactId> - <version>2.53.3-20160211.162333-3</version> + <version>2.53.3</version> </dependency> <dependency>
test/src/test/java/jenkins/CLITest.java+65 −0 added@@ -0,0 +1,65 @@ +package jenkins; + +import hudson.cli.FullDuplexHttpStream; +import hudson.model.Computer; +import hudson.model.Failure; +import hudson.remoting.Channel; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +import java.io.FileNotFoundException; +import java.net.URL; + +import static org.junit.Assert.*; + +/** + * @author Kohsuke Kawaguchi + */ +public class CLITest { + @Rule + public JenkinsRule j = new JenkinsRule(); + + /** + * Checks if the kill switch works correctly + */ + @Test + public void killSwitch() throws Exception { + // this should succeed, as a control case + makeHttpCall(); + makeJnlpCall(); + + CLI.DISABLED = true; + try { + try { + makeHttpCall(); + fail("Should have been rejected"); + } catch (FileNotFoundException e) { + // attempt to make a call should fail + } + try { + makeJnlpCall(); + fail("Should have been rejected"); + } catch (Exception e) { + // attempt to make a call should fail + e.printStackTrace(); + + // the current expected failure mode is EOFException, though we don't really care how it fails + } + } finally { + CLI.DISABLED = false; + } + } + + private void makeHttpCall() throws Exception { + FullDuplexHttpStream con = new FullDuplexHttpStream(new URL(j.getURL(), "cli")); + Channel ch = new Channel("test connection", Computer.threadPoolForRemoting, con.getInputStream(), con.getOutputStream()); + ch.close(); + } + + private void makeJnlpCall() throws Exception { + int r = hudson.cli.CLI._main(new String[]{"-s",j.getURL().toString(), "version"}); + if (r!=0) + throw new Failure("CLI failed"); + } +}
fde9c42fe05a[SECURITY-360] introduce a system switch to kill CLI
5 files changed · +85 −4
core/src/main/java/hudson/cli/CLIAction.java+1 −2 modified@@ -62,12 +62,11 @@ public String getIconFileName() { } public String getDisplayName() { - return "Jenkins CLI"; } public String getUrlName() { - return "cli"; + return jenkins.CLI.DISABLED ? null : "cli"; } public void doCommand(StaplerRequest req, StaplerResponse rsp) throws ServletException, IOException {
core/src/main/java/hudson/cli/CliProtocol2.java+1 −1 modified@@ -24,7 +24,7 @@ public class CliProtocol2 extends CliProtocol { @Override public String getName() { - return "CLI2-connect"; + return jenkins.CLI.DISABLED ? null : "CLI2-connect"; } @Override
core/src/main/java/hudson/cli/CliProtocol.java+1 −1 modified@@ -32,7 +32,7 @@ public class CliProtocol extends AgentProtocol { @Override public String getName() { - return "CLI-connect"; + return jenkins.CLI.DISABLED ? null : "CLI-connect"; } @Override
core/src/main/java/jenkins/CLI.java+17 −0 added@@ -0,0 +1,17 @@ +package jenkins; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +/** + * Kill switch to disable the entire Jenkins CLI system. + * + * Marked as no external use because the CLI subsystem is nearing EOL. + * + * @author Kohsuke Kawaguchi + */ +@Restricted(NoExternalUse.class) +public class CLI { + // non-final to allow setting from $JENKINS_HOME/init.groovy.d + public static boolean DISABLED = Boolean.getBoolean(CLI.class.getName()+".disabled"); +}
test/src/test/java/jenkins/CLITest.java+65 −0 added@@ -0,0 +1,65 @@ +package jenkins; + +import hudson.cli.FullDuplexHttpStream; +import hudson.model.Computer; +import hudson.model.Failure; +import hudson.remoting.Channel; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +import java.io.FileNotFoundException; +import java.net.URL; + +import static org.junit.Assert.*; + +/** + * @author Kohsuke Kawaguchi + */ +public class CLITest { + @Rule + public JenkinsRule j = new JenkinsRule(); + + /** + * Checks if the kill switch works correctly + */ + @Test + public void killSwitch() throws Exception { + // this should succeed, as a control case + makeHttpCall(); + makeJnlpCall(); + + CLI.DISABLED = true; + try { + try { + makeHttpCall(); + fail("Should have been rejected"); + } catch (FileNotFoundException e) { + // attempt to make a call should fail + } + try { + makeJnlpCall(); + fail("Should have been rejected"); + } catch (Exception e) { + // attempt to make a call should fail + e.printStackTrace(); + + // the current expected failure mode is EOFException, though we don't really care how it fails + } + } finally { + CLI.DISABLED = false; + } + } + + private void makeHttpCall() throws Exception { + FullDuplexHttpStream con = new FullDuplexHttpStream(new URL(j.getURL(), "cli")); + Channel ch = new Channel("test connection", Computer.threadPoolForRemoting, con.getInputStream(), con.getOutputStream()); + ch.close(); + } + + private void makeJnlpCall() throws Exception { + int r = hudson.cli.CLI._main(new String[]{"-s",j.getURL().toString(), "version"}); + if (r!=0) + throw new Failure("CLI failed"); + } +}
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
21- www.openwall.com/lists/oss-security/2016/11/12/4nvdMailing ListThird Party AdvisoryWEB
- www.openwall.com/lists/oss-security/2016/11/14/9nvdMailing ListThird Party AdvisoryWEB
- www.securityfocus.com/bid/94281nvdThird Party AdvisoryVDB Entry
- www.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class-deepsec-editionnvdThird Party AdvisoryWEB
- github.com/advisories/GHSA-2x9h-h3c4-wqqhghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2016-9299ghsaADVISORY
- wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2016-11-16nvdVendor AdvisoryWEB
- www.cloudbees.com/jenkins-security-advisory-2016-11-16nvdVendor AdvisoryWEB
- www.exploit-db.com/exploits/44642/nvdThird Party AdvisoryVDB Entry
- github.com/jenkinsci/jenkins/commit/6078dd7aa097baf3402de9d5279f6053926a1ea7ghsaWEB
- github.com/jenkinsci/jenkins/commit/ce8a2d51a5ee9ca12d0a75659b06161888e0a1bfghsaWEB
- github.com/jenkinsci/jenkins/commit/d84d9a2ad3825f316f805a18b3654b0803e0d7fcghsaWEB
- github.com/jenkinsci/jenkins/commit/f574224cae5ffde2bc4c996305c0dcf5ab135440ghsaWEB
- github.com/jenkinsci/jenkins/commit/fde9c42fe05ac925a904b6c09a81d497d0e6cceaghsaWEB
- groups.google.com/forum/ghsaWEB
- groups.google.com/forum/ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ZW2KUKYLNLVDB7STLHLYALCUFLEGCRM6ghsaWEB
- www.exploit-db.com/exploits/44642ghsaWEB
- groups.google.com/forum/nvd
- groups.google.com/forum/nvd
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/ZW2KUKYLNLVDB7STLHLYALCUFLEGCRM6/nvd
News mentions
0No linked articles in our index yet.