ROME
popgadget
1 | TemplatesImpl.getOutputProperties() |
我这样写比ysoserial上的链更简单点,感觉ysoserial上的链有一些不必要用的;比如”ObjectBean”;完全可以通过EqualsBean
本身的hashCode()
函数跳到EqualsBean.beanHashCode()
,而不需要用的”ObjectBean.hashCode”;
环境依赖
1 | <dependencies> |
分析gadget:
com.sun.syndication.feed.impl.EqualsBean
类
1 | public EqualsBean(Class beanClass,Object obj) { |
_obj
是可控制的,那么就可以调用ToStringBean.toString()
;
com.sun.syndication.feed.impl.ToStringBean
类
1 | public ToStringBean(Class beanClass,Object obj) { |
这里toString
的无参方法最终会调用的toString
的有参方法;而toString(String prefix)
方法中
在Java中,BeanIntrospector.getPropertyDescriptors(_beanClass);
是一个用来获取指定 Java Bean 类的属性描述符(PropertyDescriptor)数组的方法。PropertyDescriptor包含了Java Bean类的属性的信息,比如属性名称、属性的getter方法和setter方法等
getReadMethod()
:是 PropertyDescriptor
类中的一个方法,用于获取与当前属性相关联的读取方法(getter 方法)。而获取setter方法对应的函数就是”getWriteMethod()”;
_beanClass
和_obj
也是可以通过构造函数控制的;所以就可以调用任意对象的getter
方法,第一个想到的就是TemplatesImpl类中的.getOutputProperties()
函数;
poc
1 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; |
其中有一个坑点:
1 | ToStringBean toStringBean = new ToStringBean(TemplatesImpl.class, fakeTemplates); |
ToStringBean
类中的第一个参数不能写TemplatesImpl.class
(其实一开始我为了途方便,写的就是这个,否则反序列化不能执行代码;
“学习”;知其然知其所以然
;调试看看,为什么不行;
如果第一个参数写TemplatesImpl.class
,在使用BeanIntrospector.getPropertyDescriptors(_beanClass);
就会有五个参数符合条件;
而要利用的getOutputProperties
排在第三个,也就是数组下标为2的元素
但是在反序列化时,当遍历到第二个元素时,就会报空指针异常,所以根本不会执行到要利用的getPropertyDescriptors
函数
1 | private transient ThreadLocal _sdom = new ThreadLocal(); |
其实_sdom
属性默认是有值的,但是被transient
修饰了,所以反序列化时会为空
解决方法之一就是将ToStringBean函数的第一个参数改成Templates.class
,这样在获取javabean的(getter,setter)方法时,就只会得到getPropertyDescriptors
方法;
还有一条可以更简单的链:
gadget:
1 | TemplatesImpl.getOutputProperties() |
在com.sun.syndication.feed.impl.EqualsBean
类中的equals
调用了beanEquals
方法;而这个方法中存在pReadMethod.invoke(bean1, NO_PARAMS);
可以简单分析一下”beanEquals”函数,如果想要调用的”pReadMethod.invoke”;要使得eq = true
;否则for循环都进不去
1 | public boolean equals(Object obj) { |
可以利用CC7中的思路,使得两个HashMap相同,从而会调用第二个map的equals方法,因为HashMap没有equals方法,最终会调用HashMap的父类AbstractMap
的equals方法;
1 | public boolean equals(Object o) { |
Poc
1 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; |
最后用javassist
方法,将文件内容变小了许多;
javassist依赖
1 | <dependency> |
AspectJWeaver
依赖
1 | <dependency> |
gadget:
1 | HashSet.readObject() |
在 org.aspectj.weaver.tools.cache.SimpleCache
类中定义了一个内部类 StoreableCachingMap,这个类继承了 HashMap,提供了将 Map 中值写入文件中的功能。
文件名,路径名和内容都是可控的;
1 |
|
示例:利用反射在当前目录下向Test.txt写了内容为”zIxyd…”;
1 | String fileName = "Test.txt"; |
LazyMap
中的get
函数调用了put
函数;到这里利用链就很明显了(前一部分用CC6,后一部分从LazyMap.get调用的StoreableCachingMap.put);
1 | public Object get(Object key) { |
Poc
1 | import org.apache.commons.collections.functors.ConstantTransformer; |
C3P0
依赖
1 | <dependency> |
gadget:
1 | PoolBackedDataSourceBase.readObject() |
PoolBackedDataSourceBase.readObject()
;如果变量 o
是否是 IndirectlySerialized
类或其子类的实例。就会调用 ReferenceIndirector.getObject()
1 | private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException |
但是IndirectlySerialized
没有Serializable接口,注意writeObject
函数:当对象不能反序列时;会调用ReferenceIndirector.indirectForm
函数将不能反序列化的对象封装;IndirectlySerialized
接口的唯一实现类就是ReferenceSerialized;
1 | private void writeObject( ObjectOutputStream oos ) throws IOException |
注意indirectForm
函数;会将传递的对象强转成Referenceable
接口并调用getReference()
方法,最终返回一个Reference
对象;
也就是说ref
对象,是可控的,这一点对后面的利用非常重要;
所以最终:一个不可序列化的对象经过序列化会封装成ReferenceSerialized
对象;之后再反序列化就调用到ReferenceSerialized.getObject()
方法
调用到 ReferenceableUtils.referenceToObject()
;可以看到使用了 URLClassLoader 从 URL 中加载了类并实例化;
ref
再上面说过,因为是可控的,所以最后可以使用 URLClassLoader 从 URL 中加载了类并实例化。
Poc
1 | import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase; |