java反序列化--URLDNS
java反序列化--URLDNS
VVkladg0rJAVA反序列化–URLDNS
学习java反序列化,先从URLDNS开始
前置
我们先来了解一些java反序列化的通识知识
readObject 和 writeObject
首当其冲的就是我之前提到的readObject
和 writeObject
这是java反序列化中最重要的两个函数
这里在阐述一下:
在Java的序列化(serialization)和反序列化(deserialization)过程中,readObject
和 writeObject
方法起着特殊的作用,允许开发者自定义序列化和反序列化的行为。这两个方法并不是Serializable
接口的一部分,但它们是在java.io.Serializable
接口的实现类中经常被重写的两个私有方法。
writeObject 方法:
writeObject
方法用于在序列化过程中向输出流(通常是ObjectOutputStream
)写入自定义数据。当你重写这个方法时,你可以在序列化过程中添加额外的数据,或者改变字段的序列化顺序。通常,你会首先调用s.defaultWriteObject()
来序列化对象的所有非瞬态(non-transient)字段,然后添加额外的序列化逻辑。
例:
private void writeObject(ObjectOutputStream s) throws IOException { |
readObject 方法:
readObject
方法用于在反序列化过程中从输入流(通常是ObjectInputStream
)读取自定义数据。当你重写这个方法时,你可以在反序列化过程中读取额外的数据,或者改变字段的反序列化逻辑。同样,你通常会首先调用s.defaultReadObject()
来反序列化对象的所有非瞬态字段,然后添加额外的反序列化逻辑。
例:
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { |
注:
writeObject
和readObject
方法必须是私有的,并且它们的参数类型必须是ObjectOutputStream
和ObjectInputStream
。这是因为这两个方法是Java序列化机制通过反射调用的特殊方法。- 这两个方法不应该由类的外部代码直接调用。它们仅在Java的序列化/反序列化机制内部使用。
gadget
大家读文章的时候经常见到gadget 这里简单说下
利⽤链也叫“gadget chains”,我们通常称为gadget
。如果你学过PHP反序列化漏洞,那么就可以将 gadget理解为⼀种⽅法,它连接的是从触发位置开始到执⾏命令的位置结束,在PHP⾥可能 是 __desctruct
到 eval
ysoserial
这就是我们之前分析RMI后的利用工具
它是一个里程碑试的工具ysoserial
反序列化漏洞在各个语⾔⾥本不是⼀个新鲜的名词但2015年Gabriel Lawrence (@gebl)
和Chris Frohoff (@frohoff)
在AppSecCali上提出了利⽤Apache Commons Collections
(cc链)来构造命令执⾏的利⽤ 链,并在年底因为对Weblogic、JBoss、Jenkins等著名应⽤的利⽤,⼀⽯激起千层浪,彻底打开了⼀⽚ Java安全的蓝海
⽽ysoserial就是两位原作者在此议题中释出的⼀个⼯具,它可以让用户根据自己选择的利用链,生成反序列化利用数据,通过将这些数据发送给目标,从而执行用户预先定义的命令
使用:
生成cc这个gadget对应的POC:
java -jar ysoserial-master-30099844c6-1.jar CommonsCollections1 "id" |
ysoserial⼤部分的gadget的参数就是⼀条命令,⽐如这⾥是 id
。⽣成好的POC发送给⽬标,如果⽬标存在反序列化漏洞,并满⾜这个gadget对应的条件,则命令 id
将被执⾏
URLDNS
理解
好 接下来我可以说一下URLDNS了
URLDNS链是Java安全中比较简单的一条利用链,无需使用任何第三方库,全依靠Java内置的一些类实现,但无法进行命令执行,只能实现对URl的访问探测(发起DNS请求),并且不限制Java版本,可以用于检测是否存在反序列化漏洞,理解好URLDNS链,那么接下来对CC链的学习就会简单许多
在ysoserial
就有URLDNS的利用链了
但准确来说,这个其实不能称作“利⽤链”。因为其参数不是⼀个可以“利⽤”的命令,⽽仅为⼀个URL,其能触发的结果也不是命令执⾏,⽽是⼀次DNS请求
检测反序列化漏洞:
- 使⽤Java内置的类构造,对第三⽅库没有依赖
- 在⽬标没有回显的时候,能够通过DNS请求得知是否存在反序列化漏洞
利用链分析
我们可以通过ysoserial
的URLDNS利用链来看看是怎么个事
看到 URLDNS 类的 getObject ⽅法,ysoserial会调⽤这个⽅法获得Payload。这个⽅法返回的是⼀个对象,这个对象就是最后将被序列化的对象,在这⾥是 HashMap
。
我们前⾯说了,触发反序列化的⽅法是 readObject
,因为Java开发者(包括Java内置库的开发者)经常会在这⾥⾯写⾃⼰的逻辑,所以导致可以构造利⽤链
那么我们就来看看这个HashMap
我们在IDEA中创建一个java类 并在里面的主函数中写下HashMap
CTRL+左键点进去
注意 旁边有个结构 我们点开可以看到这个包里的所有函数 类 方便我们寻找
现在我们直接找HashMap 类的 readObject ⽅法
审计后我们可以发现将 HashMap 的键名计算了hash
putVal(hash(key), key, value, false, false) |
既然计算了hash 那我们就看看hash里面是个怎么个事 点进去(ctrl+左键 后面就都是点进去了 )
也是在hashmap包中
static final int hash(Object key) { |
可以看到hash方法是调用了hashcode()方法 点进去
是Object包中的一个方法
URLDNS 中使⽤的这个key是⼀个 java.net.URL
对象,我们看看其 hashCode ⽅法:
在我们建的这个包里访问URL 点进去
里面同样是有个hashCode()方法 看看
当hashCode的值不为-1是才会继续往下执行
而hashCode的默认值就是-1
此时, handler 是 URLStreamHandler 对象(的某个⼦类对象),继续跟进其 hashCode ⽅法:
protected int hashCode(URL u) { |
可以看到这⾥有调⽤ getHostAddress
⽅法
我这里跟出来是这样的
但是 也可能跟出来是这样的:
版本问题 不影响
我们⽤⼀些第三⽅的反连平台就可以查看到这次请求,证明的确存在反序列化漏洞(DNS外带验证)
所以,整个 URLDNS 的Gadget其实清晰⼜简单:
- HashMap->readObject()
- HashMap->hash()
- URL->hashCode()
- URLStreamHandler->hashCode()
- URLStreamHandler->getHostAddress()
- InetAddress->getByName()
要构造这个Gadget,只需要初始化⼀个 java.net.URL
对象,作为 key 放在 java.util.HashMap 中;然后,设置这个 URL 对象的 hashCode 为初始值 -1 ,这样反序列化时将会重新计算 其 hashCode ,才能触发到后⾯的DNS请求,否则不会调⽤ URL->hashCode()
另外,ysoserial为了防⽌在⽣成Payload的时候也执⾏了URL请求和DNS查询,所以重写了⼀ 个 SilentURLStreamHandler
类,这不是必须的
这里也有个其他的exp通过利用URLDNS来进行DNSlag
package org.example; |