feat: support servletNameFilter for TomcatFilterProbe

This commit is contained in:
ReaJason
2026-01-12 02:12:34 +08:00
parent 86738e308d
commit 9d5c5a1ddf
35 changed files with 348 additions and 212 deletions
@@ -0,0 +1,52 @@
package com.reajason.javaweb.probe.payload;
import com.reajason.javaweb.GenerationException;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.probe.payload.filter.*;
import com.reajason.javaweb.utils.CommonUtil;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.codec.binary.Base64;
/**
* @author ReaJason
* @since 2026/1/11
*/
public class FilterProbeFactory {
public static String getBase64ByServer(String server) {
try (DynamicType.Unloaded<?> unloaded = new ByteBuddy()
.redefine(getFilterClass(server))
.visit(TargetJreVersionVisitorWrapper.DEFAULT)
.name(CommonUtil.generateClassName()).make()) {
return Base64.encodeBase64String(unloaded.getBytes());
}
}
private static Class<?> getFilterClass(String server) {
switch (server) {
case Server.Tomcat:
case Server.JBoss:
case Server.BES:
case Server.TongWeb:
return TomcatFilterProbe.class;
case Server.Jetty:
return JettyFilterProbe.class;
case Server.Apusic:
return ApusicFilterProbe.class;
case Server.GlassFish:
case Server.InforSuite:
return GlassFishFilterProbe.class;
case Server.WebSphere:
return WebSphereFilterProbe.class;
case Server.WebLogic:
return WebLogicFilterProbe.class;
case Server.Undertow:
return UndertowFilterProbe.class;
default:
throw new GenerationException("filterProbe not supported for server: " + server);
}
}
}
@@ -13,77 +13,83 @@ public class TomcatFilterProbe {
@Override
public String toString() {
String msg = "";
StringBuilder msg = new StringBuilder();
Map<String, List<Map<String, String>>> allFiltersData = new LinkedHashMap<String, List<Map<String, String>>>();
Set<Object> contexts = null;
try {
contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
msg.append("context error: ").append(getErrorMessage(throwable));
}
if (contexts == null || contexts.isEmpty()) {
msg += "context not found\n";
msg.append("context not found\n");
} else {
for (Object context : contexts) {
String contextRoot = getContextRoot(context);
List<Map<String, String>> filters = collectFiltersData(context);
allFiltersData.put(contextRoot, filters);
try {
List<Map<String, String>> filters = collectFiltersData(context);
allFiltersData.put(contextRoot, filters);
} catch (Throwable e) {
msg.append(contextRoot).append(" failed ").append(getErrorMessage(e)).append("\n");
}
}
msg += formatFiltersData(allFiltersData);
msg.append(formatFiltersData(allFiltersData));
}
return msg;
return msg.toString();
}
private List<Map<String, String>> collectFiltersData(Object context) {
@SuppressWarnings("unchecked")
private List<Map<String, String>> collectFiltersData(Object context) throws Exception {
Map<String, Map<String, Object>> aggregatedData = new LinkedHashMap<>();
try {
Object[] filterMaps = (Object[]) invokeMethod(context, "findFilterMaps");
if (filterMaps == null || filterMaps.length == 0) return Collections.emptyList();
Object[] filterMaps = (Object[]) invokeMethod(context, "findFilterMaps");
if (filterMaps == null || filterMaps.length == 0) return Collections.emptyList();
Object[] filterDefs = (Object[]) invokeMethod(context, "findFilterDefs");
Object[] filterDefs = (Object[]) invokeMethod(context, "findFilterDefs");
for (Object fm : filterMaps) {
String name = (String) invokeMethod(fm, "getFilterName");
if (name == null) continue;
if (!aggregatedData.containsKey(name)) {
String filterClass = "N/A";
if (filterDefs != null) {
for (Object def : filterDefs) {
if (!name.equals(invokeMethod(def, "getFilterName"))) continue;
String cls = (String) invokeMethod(def, "getFilterClass");
if (cls == null) {
Object config = invokeMethod(context, "findFilterConfig", new Class[]{String.class}, new Object[]{name});
Object filter = config != null ? invokeMethod(config, "getFilter") : null;
if (filter != null) cls = filter.getClass().getName();
}
if (cls != null) filterClass = cls;
break;
}
}
Map<String, Object> info = new HashMap<>();
info.put("filterName", name);
info.put("filterClass", filterClass);
info.put("urlPatterns", new LinkedHashSet<String>());
info.put("servletNames", new LinkedHashSet<String>());
aggregatedData.put(name, info);
}
Map<String, Object> info = aggregatedData.get(name);
String[] urls = null;
try {
urls = (String[]) invokeMethod(fm, "getURLPatterns");
} catch (Exception e) {
try {
Object urlPattern = getFieldValue(fm, "urlPattern");
if (urlPattern instanceof String) {
urls = new String[] { (String) urlPattern };
}
} catch (Exception ignored) {
for (Object fm : filterMaps) {
String name = (String) invokeMethod(fm, "getFilterName");
if (name == null) continue;
if (!aggregatedData.containsKey(name)) {
String filterClass = "N/A";
if (filterDefs != null) {
Object filterDef = invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{name});
filterClass = (String) invokeMethod(filterDef, "getFilterClass");
if (filterClass == null) {
Object filterConfig = invokeMethod(context, "findFilterConfig", new Class[]{String.class}, new Object[]{name});
Object filter = invokeMethod(filterConfig, "getFilter");
if (filter != null) filterClass = filter.getClass().getName();
}
}
if (urls != null) ((Set<String>) info.get("urlPatterns")).addAll(Arrays.asList(urls));
Map<String, Object> info = new HashMap<>();
info.put("filterName", name);
info.put("filterClass", filterClass);
info.put("urlPatterns", new LinkedHashSet<String>());
info.put("servletNames", new LinkedHashSet<String>());
aggregatedData.put(name, info);
}
} catch (Exception ignored) {}
Map<String, Object> info = aggregatedData.get(name);
String[] urls = null;
try {
urls = (String[]) invokeMethod(fm, "getURLPatterns");
} catch (Exception e) {
Object urlPattern = invokeMethod(fm, "getURLPattern");
if (urlPattern instanceof String) {
urls = new String[]{(String) urlPattern};
}
}
if (urls != null) ((Set<String>) info.get("urlPatterns")).addAll(Arrays.asList(urls));
String[] servletNames = null;
try {
servletNames = (String[]) invokeMethod(fm, "getServletNames");
} catch (Exception e) {
Object servletName = invokeMethod(fm, "getServletName");
if (servletName instanceof String) {
servletNames = new String[]{(String) servletName};
}
}
if (servletNames != null) ((Set<String>) info.get("servletNames")).addAll(Arrays.asList(servletNames));
}
List<Map<String, String>> result = new ArrayList<>();
for (Map<String, Object> entry : aggregatedData.values()) {
Map<String, String> finalInfo = new HashMap<>();
@@ -91,6 +97,8 @@ public class TomcatFilterProbe {
finalInfo.put("filterClass", (String) entry.get("filterClass"));
Set<?> urls = (Set<?>) entry.get("urlPatterns");
finalInfo.put("urlPatterns", urls.isEmpty() ? "" : urls.toString());
Set<?> servletNames = (Set<?>) entry.get("servletNames");
finalInfo.put("servletNames", servletNames.isEmpty() ? "" : servletNames.toString());
result.add(finalInfo);
}
return result;
@@ -113,6 +121,7 @@ public class TomcatFilterProbe {
appendIfPresent(output, "", info.get("filterName"), "");
appendIfPresent(output, " -> ", info.get("filterClass"), "");
appendIfPresent(output, " -> URL:", info.get("urlPatterns"), "");
appendIfPresent(output, " -> Servlet:", info.get("servletNames"), "");
output.append("\n");
}
}
@@ -96,8 +96,9 @@ public class CommonUtil {
return className.substring(0, className.lastIndexOf("."));
}
public static String generateShellClassName() {
return getRandomPackageName() + ".ErrorHandler";
public static String generateClassName() {
String randomString = getRandomString(5);
return getRandomPackageName() + ".Error" + randomString.substring(0, 1).toUpperCase() + randomString.substring(1).toLowerCase() + "Handler";
}
public static String generateInjectorClassName() {
@@ -30,8 +30,8 @@ class CustomShellGeneratorTest {
void testListener() {
byte[] bytes = new ByteBuddy()
.redefine(CommandListener.class)
.name(CommonUtil.generateShellClassName()).make().getBytes();
String className = CommonUtil.generateShellClassName();
.name(CommonUtil.generateClassName()).make().getBytes();
String className = CommonUtil.generateClassName();
ShellConfig shellConfig = ShellConfig.builder()
.server(Server.Tomcat)
.shellType(ShellType.LISTENER)
@@ -52,8 +52,8 @@ class CustomShellGeneratorTest {
void testFilter() {
byte[] bytes = new ByteBuddy()
.subclass(Object.class)
.name(CommonUtil.generateShellClassName()).make().getBytes();
String className = CommonUtil.generateShellClassName();
.name(CommonUtil.generateClassName()).make().getBytes();
String className = CommonUtil.generateClassName();
ShellConfig shellConfig = ShellConfig.builder()
.shellType(ShellType.FILTER)
.build();
@@ -72,8 +72,8 @@ class CustomShellGeneratorTest {
void testValue() {
byte[] bytes = new ByteBuddy()
.redefine(GodzillaValve.class)
.name(CommonUtil.generateShellClassName()).make().getBytes();
String className = CommonUtil.generateShellClassName();
.name(CommonUtil.generateClassName()).make().getBytes();
String className = CommonUtil.generateClassName();
ShellConfig shellConfig = ShellConfig.builder()
.server(Server.BES)
.shellType(ShellType.VALVE)
@@ -33,7 +33,7 @@ class GodzillaGeneratorTest {
.build();
GodzillaConfig godzillaConfig = GodzillaConfig.builder()
.shellClass(GodzillaServlet.class)
.shellClassName(CommonUtil.generateShellClassName())
.shellClassName(CommonUtil.generateClassName())
.pass("pass")
.key("key")
.headerName("User-Agent")