前言

继续学习Java安全,这里开始分析下神器ysoserial的代码逻辑,学一下它其中的gadget的写法和逻辑,这里先从最简单的URLDNS这个调用链开始吧,因为这个gadget不依赖其他库,只需要java基础jdk,然后虽然不能够rce类似,但是可以在没有回显的情况下通过dns查询来判断是否有反序列化漏洞(之前写的文章,最近在秋招没空忘记发了)。

正文

我们先git clone下来ysoserial然后在idea中打开,并同步pom.xml中的依赖,然后我们观察pom.xml可以发现整个程序的入口在ysoserial.GeneratePayload处,我们看一下其中的主要逻辑

看到拿到payload类型和命令参数,getPayloadClass拿到Class类,newInstance()拿到实例,然后调用实例的getObject方法拿到调用链,最后利用Serializer.serialize()方法序列化调用链并输出,这样我们就拿到了序列化的数据,然后我们就可以将这个已经序列化的数据向可能存在漏洞即反序列点的地方打,类似下图就生成了一个URLDNS调用链的序列化payload

可以用xxd看下大概内容

可以看到很明显的aced开头的java序列化标志,将这个发送给可能存在反序列化漏洞的地方就会给我们准备的反连网址发送一个dns查询,这里我们可以用知道创宇的ceye反连平台。

我们可以自己写一个反序列化的demo来测试

Run后成功检测到dns

或者直接在URLDNS那个类中配置下参数为反连url,然后我们断点调试下,我们先看下整个的代码,还是比较少的

可以看到主要的逻辑就是getObject方法实现的,main函数是进行调用序列化并且反序列化一次,然后SilentURLStreamHandler是为了防止在生成Payload的时候也产生请求就行了重写不太重要,我们主要看下getObject方法

看到生成一个HashMap对象,然后在设置相应的URL对象,然后put方法设置key,value,value没有用随便什么都可以,然后key才是会触发dns的关键,然后利用反射设置hashCode为-1,这样会清楚上面生成payload计算的hashCode,这样的话在反序列化的时候才会重新计算hashCode并因此来触发DNS查询。

根据注释我们跟进下HashMap类,然后我们知道反序列化的触发点关键函数就是readObject方法,所以我们直接跟进到readObject方法,然后看到最后putVal()处(也是根据注释)hash函数重新根据key计算hashcode,所以在这里打上断点后开启debug,可以看到调用栈

后面几个函数是我跟进的先别急,我们从readObject来看,前面的是反序列化和反射的一些东西,一直触发到HashMap中的readObject方法,然后跟进到putVal处

继续跟进hash()函数,调用key.hashCode函数

继续跟进到handler.hashCode函数,这个handler是URLStreamHandler类,所以继续跟进到URLStreamHandler类中的hashCode方法,可以看到getHostAddress方法来解析域名,

最后getByName成功发出dns请求,整个调用结束

总结

URLDNS是最简单的一个Gadget了,然后也不需要第三方的依赖就可以利用,常常利用在例如shiro的检测。