Apache Commons Collections<=3.2.1反序列化漏洞是Java反序列化的经典漏洞,Apache-commons-collections组件为Java提供了很多基础常用且强大的数据结构,方便开发。本次分析的版本选择3.1。
Apache Commons Collections反序列化漏洞的最早的commit记录是2015年1月29日,说明这个漏洞可能早在2014年甚至更早就已经被人所利用,利用方式被人公开后直接引发了Java生态系统的大地震,与此同时Java反序列化漏洞仿佛掀起了燎原之势,无数的使用了反序列化机制的Java应用系统惨遭黑客疯狂的攻击,为企业安全甚至是国家安全带来了沉重的打击!(摘自javasec.org)
环境搭建
Apache Commons Collections<=3.2.1反序列化漏洞是Java反序列化的经典漏洞,Apache-commons-collections组件为Java提供了很多基础常用且强大的数据结构,方便开发。本地分析的版本选择3.1。
创建一个简单的maven项目(比如maven-archetype-quickstart
):
在pom.xml
添加依赖:
1 | <dependency> |
漏洞分析
触发点
定位到org.apache.commons.collections
。
漏洞的主要问题出现在InvokerTransformer
类的transform
方法,这个类实现了Transformer
和Serializable
接口,意味着这个类是可以被序列化的。
Transformer
接口定义了将输入对象转换为输出对象的方法,如下:
InvokerTransformer
类对transform
方法进行了实现,如下:
根据InvokerTransformer
类的实现方式我们可以知道,大致逻辑为会获取输入类的类对象,根据输入的方法名(iMethodName
)和参数类型(iParamTypes
)获取指定的反射方法对象,接下来反射调用指定的方法并返回方法调用结果。
通过刚才的分析,可以发现直接使用InvokerTransformer
类便可进行命令执行。
代码如下:
1 | public static void main(String[] args) { |
真实场景中,没法在调用transformer.transform
的时候直接传入Runtime.getRuntime()
对象的。但这也在另一方面说明此处可以触发命令执行的漏洞,接下来开始寻找利用链。
寻找利用链
接下来是另外一个重要的类ChainedTransformer
,该类同样实现了Transformer
和Serializable
接口,也可以被序列化。
该类对transform
方法的实现逻辑如下:
从实现逻辑上可以看出,该方法会对iTransformers
数组中的对象依次调用其transform
方法。并将上一次调用的结果对象传入。
接来下需要考虑如何将Runtime
对象传入,作为初始的object
被InvokerTransformer
的transform
方法调用。
恰巧ConstantTransformer
类存在transform
方法,并直接返回构造时传入的对象。
代码如下:
利用上面提到的几个类,构造如下利用链:
1 | public static void main(String[] args) { |
大致逻辑为首先调用ConstantTransformer
类的transform
方法返回java.lang.Runtime
对象,
接下来调用InvokerTransformer
类的transform
方法获取java.lang.reflect.Method
的方法对象,方法为getRuntime
,
再次调用InvokerTransformer
类的transform
方法反射调用getRuntime
方法,获取Runtime
类的实例,
再次调用InvokerTransformer
类的transform
方法执行Runtime
类的exec
方法,进行命令执行。
对上述调用过程进行简化,代码和输入结果如下:
思考
通过输出结果,可以发现有两次java.lang.Runtime
对象,为何不直接调用exec
方法进行命令执行?
通过查看Runtime
类的代码可知,Runtime
类的构造方法为私有,只能通过静态方法获取实例。很明显的单例模式。
代码如下:
因此第一次获取的java.lang.Runtime
对象并未实例化,无法执行exec
方法。
1 | 代码: |
触发利用链-1
上述分析解决了如何利用漏洞触发点并构造利用链进行命令执行,现在已经使用InvokerTransformer
触发点创建了一个含有恶意调用链的Transformer
类的Map对象,接着我们应该思考如何才能够将调用链窜起来并执行。
TransformedMap
类间接的实现了java.util.Map
接口,同时支持对Map
的key
或者value
进行Transformer
转换,调用decorate
和decorateTransform
方法就可以创建一个TransformedMap
。
只要调用TransformedMap
的setValue/put/putAll
中的任意方法都会调用InvokerTransformer
类的transform
方法,从而也就会触发命令执行。
使用TransformedMap
类的setValue
触发transform
示例:
1 | public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException { |
上述代码向我们展示了只要在Java的API中的任何一个类实现了java.io.Serializable
接口,并且可以传入我们构建的TransformedMap
对象还要有调用TransformedMap
中的setValue/put/putAll
中的任意方法一个方法的类,我们就可以在Java反序列化的时候触发InvokerTransformer
类的transform
方法实现RCE
。
触发利用链-2
除了TransformedMap
类触发反序列漏洞,ysoserial还提供了多种基于InstantiateTransformer/InvokerTransformer
构建调用链方式:LazyMap、PriorityQueue、BadAttributeValueExpException、HashSet、Hashtable、AnnotationInvocationHandler等。
地址(带有CommonsCollections的文件):
1 | https://github.com/frohoff/ysoserial/tree/master/src/main/java/ysoserial/payloads |
参考
1 | https://javasec.org/javase/JavaDeserialization/Collections.html |