如何从零开始分析并Tomcat内存马中的Filter原理?

摘要:代码审计 | Filter —— Tomcat 内存马从零到注入 目录 一、快速搭建环境 二、Filter 是什么,正常怎么用 代码解释 web.xml 方式注册 Filter 两种静态注册方式对比 三、StandardContext 中的
代码审计 | Filter —— Tomcat 内存马从零到注入 目录 一、快速搭建环境 二、Filter 是什么,正常怎么用 代码解释 web.xml 方式注册 Filter 两种静态注册方式对比 三、StandardContext 中的三个核心 Filter 集合 三者的协作流程 对内存马的关键意义 四、动态注册:绕过 web.xml 注入 Filter 内存马 第一步:拿到 StandardContext 第二步:定义恶意 Filter(匿名内部类) 第三步:注册 FilterDef 第四步:注册 FilterMap 第五步(关键):强制写入 filterConfigs 完整注入 JSP 与验证 五、总结 一、快速搭建环境 创建项目 先在 IDEA 里快速创建一个 Jakarta EE(原 Java EE)项目。 注意:Jakarta EE 高版本需要高版本的 JDK,这里换成低版本的 Java EE 8 就行。 导入 Tomcat 依赖 导入 Tomcat 的本地依赖 lib/,把整个 lib 目录加进去(也可以用 Maven 重新下载)。 配置热加载 修改配置支持热加载(也可以在 xml 里配置)。 如果终端出现中文乱码,可以添加启动参数 -Dfile.encoding=UTF-8 直接启动 没有报错,环境搭好了。 二、Filter 是什么,正常怎么用 先写一个最简单的 Filter 例子,比如"记录每次请求的 IP"。 package org.example.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") public class LogFilter implements Filter { @Override public void init(FilterConfig filterConfig) { System.out.println("LogFilter 初始化"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String ip = request.getRemoteAddr(); System.out.println("请求来自:" + ip); // 放行,继续执行后续 Filter 或 Servlet chain.doFilter(request, response); } @Override public void destroy() { System.out.println("LogFilter 销毁"); } } 启动 web 后随便访问一个网页 http://localhost:8080/,控制台输出了一次初始化,每次请求都会多一条记录。 点击红色方框停止服务。 显示 LogFilter 销毁。 代码解释 @WebFilter("/*") 告诉 Servlet 容器(Tomcat)这是一个过滤器,并且拦截所有 URL 路径(/* 表示任意路径)。这样就不需要写 web.xml 配置了。 内存马关联:后面要做的就是不使用注解,也不写 web.xml,直接通过反射把恶意 Filter 塞进 Tomcat 内部的那个管理结构中。 @Override 告诉编译器下面的方法是重写接口,不是自己新建的。因为 Filter 接口里已经声明了 init、doFilter、destroy 三个方法(public class LogFilter implements Filter 里的 implements Filter 就是实现接口的意思)。 extends 和 implements 的区别:继承(extends) 用于类与类之间,子类继承父类的属性和方法;实现(implements) 用于类与接口之间。 加了 @Override 的好处是:如果把 init 写成了 int,编译器会直接报错,因为 Filter 接口里没有这个方法。不写的话不会报错,照样跑,但可能出现隐蔽的 bug。
阅读全文