JAVA安全|Gadget篇:URLDNS链

admin 2022年10月1日12:51:56评论117 views字数 5052阅读16分50秒阅读模式
0x00  前言
    JAVA安全系列文章主要为了回顾之前学过的java知识,构建自己的java知识体系,并实际地将java用起来,达到熟练掌握java编程,并能用java编写工具的目的。此系列文章需要读者具备一定java基础,不定时更新。相关详情可通过我的公众号文章进行查看:
JAVA安全|即将开启:java安全系列文章
    Gadget篇主要是分析一些经典常见的反序列化链,基本来自于Ysoserial工具。与网上大部分分析反序列化链文章的不同点在于我会尽可能地从如何发现链子的角度来讲解,参考的资料主要为B站白日梦组长的视频以及P牛的JAVA安全漫谈系列文章。
    本文为JAVA安全系列文章第七篇。

0x01  前情回顾总结

    前面我们学习了JAVA原生反序列并通过一个小靶场体验了代码分析-->POC编写-->白盒寻找gadgets chain-->利用反序列化getshell这样的一系列流程。还学习了JAVA安全中很重要的反射机制。

    概括来说,JAVA原生序列化就是利用Java.io.ObjectOutputStream对象输出流的writeObject(Object obj) 方法将一个实现了Serializable或者Externalizable接口的类的对象转化为字节序列;而JAVA原生反序列化就是利用Java.io.ObjectInputStream对象输入流的readObject() 方法将一个字节序列恢复为对象。java原生序列化数据的16进制以aced0005开头,base64编码是以rO0AB开头。开发者通常会重写writeObject、readObject方法来自定义序列化与反序列化过程。关于反序列化的安全问题从根本上来说得从readObject()方法做文章,反序列化产生漏洞的形式有以下四种方式:

1.入口类的readObject直接调用危险方法(很理想的状态,多见于自娱自乐)2.入口类参数中包含可控类,该类有危险方法,readObject时调用3.入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用4.构造函数/静态代码块等类加载时隐式执行

    JAVA反射机制原理是由于JAVA类加载机制的特点,当进行类加载时会在堆区产生该类的Class对象(有且只有一个,可以理解为复制出来的该类的原型),该对象包含了类的完整结构信息(属性、方法、内部类等),它是一种数据结构,可通过一系列API获取类的完整结构。反射的作用:

使得JAVA语言具有动态性修改已有对象的属性动态生成对象动态调用方法操作内部类和私有方法

    反射淋漓尽致地体现了JAVA语言万物皆对象的特点,在反射面前一切都是纸老虎。突破限制,这就是JAVA反射。


0x02  如何寻找Gadget的几点思考

    试想下假如我们要找一条比较通用的链子,那我们该如何去考虑这个问题?
    第一,一条完整的链,需要有入口,有出口,还有中间过程。入口就是入口类,专业术语叫source,必须要重写readObject() 方法;出口就是这条链子走完到最后产生的结果是啥,这是由最后这个类所执行的方法决定的,出口就是执行类,专业术语叫sink;中间过程就是调用链,专业术语gadget chain,经过层层套娃,顺利地从入口类的readObject() 方法走到执行类的某个方法,这是最繁琐的过程,可能找了半天,构造了半天,最后发现不行。
    第二,这条链子上的类都要实现Serializable或者Externalizable接口;其次,要具备比较通用的特点,那这条链上的类最好是jdk自带或者JAVA常用组件里有
   第三,入口类,构造该类对象和调用其方法时参数类型广泛最好是Object类型,方便我们传入对象;且readObject()方法中调用常见方法。结合参数类型广泛和调用常见方法这两点,我们才好从入口类的readObject()方法进入到另一个类的方法,有点移花接木,狸猫换太子的感觉。
   第四,调用链,前一个类调用的方法在后一个类中有,且前一个类的方法的参数类型为后一个类的类型,简单的说就是相同方法名,相同类型(比如CC1链中就是一系列实现了Transformer接口的类,调用类的transform()方法)。这样才可层层套娃,移花接木,狸猫换太子,精心构造,精妙绝伦。
   第五,执行类,这是最重要的。费了那么大劲,最后产生的危害有多大就看这个了。危害可以是SSRF、写文件、拒绝服务等,当然我们最希望的是RCE。
说了那么多,总结下来就是:
共同条件:实现Serializable或者Externalizable接口,最好是jdk自带或者JAVA常用组件里有入口类source:(重写readObject 调用常见函数 参数类型宽泛 最好jdk自带)调用链gadget chain:相同方法名、相同类型执行类sink:RCE SSRF 写文件等等
以上来自白日梦组长以及自己最近学CC链的体会总结,如有不对的地方,望指正(抱拳)。我会慢慢完善它。还不理解的伙伴,动手练习思考,就会有所体会了。


0x03  URLDNS链的发现与分析

1.URLDNS链介绍

    URLDNS是JAVA复杂的反序列化链中最简单的一条,它不是一条真正意义上的“利⽤链”。因为它所能产生的结果不是命令执⾏,⽽是⼀次DNS请求。既然不能利用,那么为什么我们还要学习它呢?

    第一,因为它足够简单,很适合新手,反序列化链的学习不建议一上来就分析CC链;第二,因为它使⽤Java内置的类构造,对第三⽅库没有依赖,对JDK版本没有要求,且其结果是发起一次DNS请求,不管目标有无回显,都⾮常适合用来检测是否存在反序列化漏洞。


2.URLDNS链的发现

    根据上面对入口类的描述,对JAVA比较熟悉的话,首先就会想到Map系列(就是Python里的字典,是键值对),天然的理想的入口类,Map里常见的就是HashMap了。我们来看下HashMap的readObject()方法:

JAVA安全|Gadget篇:URLDNS链

    我们重点关注的应该是最后这几行代码:

JAVA安全|Gadget篇:URLDNS链

    这几行代码是循环读取键值对,然后利用putVal()方法将其存入HashMap中,而putVal()方法中又对键值执行了hash()方法,我们ctrl+b进入这个方法:

JAVA安全|Gadget篇:URLDNS链

    可以看到该方法需要传入的key的参数类型为Object即任意类的对象,我们很容易就可以套一个娃进去。该方法是判断键值是否为空,为空返回0,不为空则会对键值执行hashCode()即计算hash值。而hashCode()是很常见的方法,所有继承了Object的类都会有这个方法。

    接下来如果我们要找调用链上的类,选择就很多了,jdk自带的实现了Serializable或者Externalizable接口的类有很多。毫无目的地从调用链到最后的执行某个操作这样正向找肯定很难,我们逆着来。先思考下我们想要产生的结果,首选当然是RCE,JAVA中执行代码相关的语句就是Runtime.getRuntime().exec()了,但Runtime这个类没有实现Serializable或者Externalizable接口:

JAVA安全|Gadget篇:URLDNS链

挖RCE的想法gg。退而求其次,找SSRF,也即发起一个URL请求。jdk中与此相关的类就是java.net.URL了。我们来看下URL这个类:

JAVA安全|Gadget篇:URLDNS链

实现了Serializable接口,可以作为执行类。其openConnection()方法可以发起URL请求:

JAVA安全|Gadget篇:URLDNS链

但该方法并不是常用方法,我们逆着去找谁调用了这个方法也不好找。那我们来看下URL的hashCode()方法,看下假如我们传入的键值是URL对象会发生什么:

JAVA安全|Gadget篇:URLDNS链

会先判断当前URL对象的hashCode是否为-1,不为-1则直接返回hashCode,如果为-1,则进入到handler.hashCode()方法。这里的-1为URL对象hashCode 的初始值,handler为URLStreamHander 对象,且带有transient,即不参与序列化:

JAVA安全|Gadget篇:URLDNS链

我们继续跟进handler.hashCode():

JAVA安全|Gadget篇:URLDNS链

调用了getHostAddress()方法,看方法名是获取主机地址,继续跟进:

JAVA安全|Gadget篇:URLDNS链

看到InetAddress.getByName(host),看方法名是根据主机名字获取网络地址,这不就是一个DNS请求么?跟到这里就不需要再跟了。这就是URLDNS链了。


3.分析

根据上面的分析,我们来捋捋,最后的DNS请求是怎么触发的:

    首先,HashMap的readObject()方法会循环读取键值对,利用putVal()方法将其存入HashMap中,而putVal()方法中又对键值执行了hash()方法,而hash()方法中若键值不为空,则会调用key的hashCode方法。其中,key的类型是Object即可以是任意对象,假如此处key的类型为URL对象,则会进入到URL的hashCode()方法,在该方法中若hashCode为初始值-1,会进入到handler.hashCode()方法重新计算hashCode(handler为URLStreamHander 对象),handler.hashCode()中会调用getHostAddress()方法,该方法会使用InetAddress.getByName(host)方法即根据主机名字获取网络地址,从而触发了DNS请求。

    上面文字描述的URLDNS gadget可以这样表示:

JAVA安全|Gadget篇:URLDNS链


0x04  POC编写

    根据上面的分析,我们来编写POC。首先我们得要创建一个URL对象,此时其hashCode值为-1;再创建一个HashMap对象,将url对象作为键值传进去,值任意。此处有坑,我们使用put方法向hashMap传入值时,会发生:

JAVA安全|Gadget篇:URLDNS链

    看到putVal()和hash()是不是很熟悉,put时url对象的hashCode为-1,由此会进入到触发DNS请求的那个逻辑,也就是说我们在put时就会产生DNS请求:

JAVA安全|Gadget篇:URLDNS链

而经过DNS请求之后,url对象的hashCode值就被重新计算,不是初始值-1了。那么接下来序列化时不是-1,反序列化出来自然也不是-1,故实际反序列化时也就不会进行DNS请求。而在我们的DNS平台上收到的请求实际上是put时发出的,会对我们判断目标是否存在反序列化漏洞形成干扰。

    故此坑的解决方法是:利用反射,在put之前将url对象的hashCode值设值成不为-1,put之后再利用反射,将其设置为-1。这样put时就不会发起DNS请求了:

JAVA安全|Gadget篇:URLDNS链

然后将其序列化,再反序列化,反序列化时收到DNS请求:

JAVA安全|Gadget篇:URLDNS链


完整代码如下:

JAVA安全|Gadget篇:URLDNS链

我们可以把这段POC修改下,使其生成Payload,将Payload放到之前的webgoat靶场中,DNSLog上收到请求:

JAVA安全|Gadget篇:URLDNS链

是不是很有趣,哈哈~


0x05  总结

    本文我们学习了最简单的一条Gadget—URLDNS链。从如何发现这条链的角度详细代码分析了该链的调用过程,以及最后的POC编写。其中使用到了反射机制,通过反射修改已有对象属性来解决put时的坑。

    该链的特点是jdk自带,且无jdk版本限制,该链的作用是检测目标是否存在反序列化漏洞。可能有的读者会有疑问,HashMap为什么要重写readObject方法呢,有默认的反序列化不行么?感兴趣的可以看下这篇文章:

https://juejin.cn/post/6844903954774491144#heading-5

    利用URLDNS检测出了漏洞,自然就要进行利用了。下一篇我们就来学习经典的CC1链。


Java安全系列文集

第0篇:JAVA安全|即将开启:java安全系列文章

第1篇:JAVA安全|基础篇:认识java反序列化

第2篇:JAVA安全|基础篇:实战java原生反序列化

第3篇:JAVA安全|基础篇:反射机制之快速入门

第4篇:JAVA安全|基础篇:反射机制之Class类

第5篇:JAVA安全|基础篇:反射机制之类加载

第6篇:JAVA安全|基础篇:反射机制之常见ReflectionAPI使用


如果喜欢小编的文章,记得多多转发,点赞+关注支持一下哦~,您的点赞和支持是我最大的动力~

原文始发于微信公众号(沃克学安全):JAVA安全|Gadget篇:URLDNS链

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年10月1日12:51:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JAVA安全|Gadget篇:URLDNS链http://cn-sec.com/archives/1329394.html

发表评论

匿名网友 填写信息