大概学了一个多月Java,感觉有点底子了以后,终于开始学习Java相关漏洞了。搓搓手 ==
开个ysoserial的新坑。。日后慢慢填 = =
ysoserial基础使用
生成POC的地方是 GeneratePayload
类的 main 方法。
而调试链子的地方为 PayloadRunner
类。每个Payload类都会有一个 main方法,会调用 PayloadRunner
类的 run 方法。该方法会将 gadgets 序列化后再反序列化回来。使得我们可以跟进链子。
URLDNS Poc
定位到 ysoserial 的 URLDNS.java
。查看 getObject()
方法
以下注释为ysoserial源码的注释翻译,现在可以不用深入理解,后文会分析为什么。
1 | public Object getObject(final String url) throws Exception { |
配置IDEA
环境:JDK1.8
配置 IDEA 的 Run/Debug Configurations:先运行 URLDNS 的 main()
函数使 IDEA Configurations 自动添加对应的配置。此时由于没有设置参数,肯定会报错,不需理会。
然后再设置 Program arguments 为Dnslog 地址:

配置完成后,再运行一次 URLDNS 的 main()
,成功触发DNS请求

调试反序列化链
通过查看 ysoserial 的注释可以得知,整个链子如下:
1 | * Gadget Chain: |
这个注释已经把链子写的很清楚了,这里就不一一跟进分析,仅说明一些关键的点,并解决下初看Poc时的疑惑。
调试链子时,我们仅需要在 HashMap.readObject()
方法的 putVal()
操作上打上断点即可调试。
关键点1-最终触发DNS解析的代码
漏洞点为 URLStreamHandler.hashCode()
。关键代码如下:
1 | protected int hashCode(URL u) { |
关键点2-为何POC重置hashCode为-1
gadgets 的最后一个方法为 URL.hashCode()
。看一下这个类和方法的代码:
1 | public final class URL implements java.io.Serializable { |
我们可以得知,URL类的 hashCode属性 并无 transient
修饰,且若 hashCode()
中 URL类 的 hashCode
值不为 -1 将会直接 return
,无法执行到跳板 handler.hashCode(this)
而在Poc的代码中,执行了这样的操作:
1 | HashMap ht = new HashMap(); |
跟进 put()
方法:
1 | HashMap |
跟进后可发现,在生成Payload这一步时就会调用URL的 hashCode()
,使得这个key值的hashCode属性不为-1。这样的数据被序列化后再反序列化回去时,由于URL hashCode属性不为-1,将无法执行到跳板 handler.hashCode(this)
所以我们需要在 POC中 执行完 Hashmap.put()
后,手动将 hashCode值 还原成-1(用反射是因为hashCode是private修饰的),确保Payload被反序列化后能够正常执行到跳板。
关键点3 - 自定义了一个继承URLStreamHandler的类
跟过链子就知道,最终是 URLStreamHandler
类触发的Dns请求。而POC中使用了多态的方式来 new 一个 URLStreamHandler类。为何如此?
根据注释可知,POC作者不希望在生成POC的时候会执行Dns请求。再根据上文关键点2可知,生成Poc的时候会调用 URL.hashCode()
,第一次生成Payload时 URLStreamHandler
的 hashCode肯定是-1,铁定会执行到最后的Dns请求代码。
为了解决这个问题。POC作者使用了多态的方式,自定义继承 URLStreamHandler
的类 SilentURLStreamHandler
,并重写其关键方法 getHostAddress()
,这样在运行Poc时只会调用到 SilentURLStreamHandler
的getHostAddress()
。
1 | public Object getObject(final String url) throws Exception { |
Reference
【代码审计】知识星球 - Java安全漫谈