Ysoserial工具-URLDNS利用链
0x01 序列化
序列化小栗子
Main.java
package com.time.Serializable; |
Person
package com.time.Serializable; |
FileOutputStream
和ObjectOutputStream
是java的流操作,可以把OutputStream
当做一个单向流出的水管,FileOutputStream
打开了文件,就相当于给文件接了一个File类型水管,然后把FileOutputStream类型对象传给了ObjectOutputStream
,相当于把File类型水管接到了Object类型水管。由于Object类是所有类的父类,所以Object类型水管可以投放任何对象,
这里创建了Person对象并传给
writeObject
方法,
相当于把Person对象扔进了Object类型水管,即 Person对象->Object类型水管->File类型水管->文件
这样就把Person对象写入了文件,java输入输出流的方式处理数据真聪明
如果我想把序列化对象写入byte数组,那就创建个byteArrayOutputStream
类型水管,然后,把它接到Object类型水管上,后面步骤不变,则:Person对象->Object类型水管->byte类型水管->byte数组 - 摘抄 FreeBuf 用户z3 Java代码审计系列
个人觉得上面解释的非常详细了,大佬YYDS
使用SerializationDumper工具查看序列化后的内容。
红框里面是类、属性名、成员变量值。
0x02 反序列化
小栗子:
Main.java
package com.time.Serializable; |
把单向流出的水管换为单向流入的(Output换为Input),然后把写入数据的writeObject换为readObject,即:序列化数据person.txt->File类型水管->Object类型水管->Object对象。
(Person)这个用法是强制类型转换,将Object转Person类型
0x03 URLDNS
URLDNS 是 ysoserial 中的基础链,不依赖第三方库,当然也不能执行任何命令,只能发送一个dnslog的请求。
ysoserial工具中给出的 URLDNS 链
HashMap.readObject() |
上面是作者给出的调用链,hashCode 中调用了 getHostAddress 来发送 dns 请求。
尝试自己写 URL.hashCode() 看看触发流程
package ysoserial.payloads; |
跟进 url.hashCode()
当 hashCode = -1 会调用 URLStreamHandler.hashCode 方法
hashCode 会调用 getHostAddress 解析我们传入的 dns 地址。
而作者给的 payload 中说明了 ht.put 也能触发调用链
测试代码
package ysoserial.payloads; |
跟进 put
可以看见调用了 putVal方法,这里传的 key和value 都是 dns 地址,并调用了 hash 处理 key
key 值不为空,调用 key.hashCode()
这里 hashCode 值默认为 -1,则会调用handler.hashCode,handler修饰符
transient 关键字表示变量不被序列化,应该是这里的hashCode值不被序列化。
跟进 hashCode
同 url.hashCode() 都调用了同一段代码,使用 getHostAddress 解析了 dns 地址。
继续看作者给的 payload
定义了 SilentURLStreamHandler 静态类,并且类中定义了 getHostAddress 方法 和 openConnection 方法。
直接测试作者给的代码,在 RUN/DEBUG 中配置如下
直接跟到 getHostAddress()
跳转到了作者编写的 getHostAddress() 返回 null,没有解析 dns
这是为什么?网上看大佬们的文章才了解,主要为了在生成 payload 期间多次执行,防干扰用的。
0x04 payload Test
package ysoserial.payloads; |
可以看见使用 for循环 将 key 和 value 分别反序列
并调用了 putVal(hash(key), key, value, false, false)
之后的流程就和上面分析的一样了
0x05 总结
这个链主要是因为 HashMap 实现了 Serializable 接口,并且重写了 readObject 和 writeObject 方法。
给 HashMap 中传入 URL 对象,然后使用 HashMap.readObject 执行反序列化操作时,会调用 putVal 方法,而 putVal 是往 HashMap 写入键值对的方法,会 调用 hash 对 key 进行 hashCode 值计算 ,由于是 URL 对象,所以最后会调用 URLStreamHandler.hashCode 来解析 DNS 地址。
JAVA反序列化RCE触发流程(三要素)
- readobject 反序列化利用点
- 利用链
- RCE触发点
0x06 参考链接
https://www.freebuf.com/articles/web/308461.html
https://mp.weixin.qq.com/s/ExcPN0HcSJROtE2_vVI6Hg
https://xz.aliyun.com/t/9417#toc-2
https://www.anquanke.com/post/id/201762
P神代码审计-Java安全漫谈-反序列化2