URLDNS链分析

Ysoserial初使用

环境搭建

项目地址:https://github.com/frohoff/ysoserial

1
git clone https://github.com/frohoff/ysoserial.git

配置maven

image.png

然后用maven自动导包进去

运行主函数,编译运行正常

image.png

处理了一个报错

当时所有的包都无法导入

https://blog.csdn.net/q5926167/article/details/119318876

终于参考这里的文章解决了

使用方法

使用yo生成反序列化payload:

方法1 IDEA添加参数&写入文件

image.png

image.png

打印出的payload不能直接用

所以在序列化完之后写入本地文件,这里序列化对象时重写了writeObject方法,就和反序列化思路一样,我们在这个方法的最后添加一段用来输出为本地文件的功能

image.png

如此payload保存到1.ser中,以便我们后续使用

方法2 打包成jar

payload测试&传值

我们尝试使用我们刚刚创建的cc来攻击本地server

jdk1.7+tomcat8.5搭建环境

写一个简单的Servlet,doPost中反序列化传来的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream is = req.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
try {
ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
ois.close();
}
}

然后在web.xml中配置路由

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>Test1</servlet-name>
<servlet-class>com.study.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Test1</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>

maven中导入存在漏洞的包:

1
2
3
4
5
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>

启动tomcat,部署页面,用curl传值

image.png

成功触发漏洞,说明环境正确,ysoserial生成payload正确

前置准备

利用ysoserial生成urldns payload

环境就是测试搭建ysoserial一文中搭建的webserver

image.png

修改参数,生成payload,然后传入webserver

可以看到有dnslog记录,复现成功,下面开始调试分析

image.png

HashMap类

HashMap的基本用法 https://blog.csdn.net/wxgxgp/article/details/79194360

HashMap的实现原理https://wiki.jikexueyuan.com/project/java-collection/hashmap.html

HashMap的数据结构:

HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体

从下图中可以看出,HashMap 底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个 HashMap 的时候,就会初始化一个数组。

image.png

调试URLDNS利用链

HashMap.readObject()

通过ysoserial和我们动态调试可以得到:

URLDNS链的起点也是根源在HashMap的readObject方法,我们知道java中,反序列化漏洞的触发入口一般都在重写的readObject方法中,因此我们从这里下手

在servelt的反序列化方法那里打上断点

image.png

跟进到readObject方法里面,在readObject0处断点

image.png

几步步过之后,来到了HashMap.readObject,这也是URLDNS的入口

image.png

下面就是在readObject方法里面动态调试

image.png

java.io.ObjectInputStream.defaultReadObject() 方法从该流中读取当前类的非静态和非瞬态字段。这也许只能称为从类反序列化readObject方法。它会抛出NotActiveException如果它被调用。

image.png

这里的判断跳过

image.png

下面是一系列初始化HashMap的操作,创建空数组,然后计算capacity,这里没有什么特别的地方

image.png

开始遍历键值对

HashMap.putForCreate()

这里步入

HashMap.putForCreate()方法

image.png

可以看到是计算哈希的方法,我们步入这个hash方法

第一次分析的时候,认为hash方法可能是个没啥用就计算的方法,没有跟进去看,略过了。

所以要细心,不能遗漏

HashMap.hash()

image.png

我们dns查询的网址被当作一个URL对象传入

这里hashSeed为0,略过那个if

步入下面的362行的hashCode方法

java.net.URL.hashCode()

image.png

hashCode != -1

878行的判断条件hashCode != -1

当我们正常传入反序列化对象时,他会在反序列化读取给key赋值的时候,改变hashCode的值

image.png

因此在构造payload的时候要手动让这个条件为true

步入这里的hashCode方法

java.net.URLStreamHandler.hashCode

image.png

根据绿字描述,这个方法是根据URL对象的不同协议和需求,生成一个供哈希表索引的一个hashcode返回

image.png

根据协议计算hashCode加到h上

hashCode方法就是位运算生成hash值

image.png

步入他获取域名的方法

image.png

java.net.URLStreamHandler.getHostAddress

image.png

第一个if判断跳过,然后getHost方法获取host

跟进getHost方法

image.png

步入getByName方法

image.png

java.net.InetAddress.getByName(java.lang.String)

image.png

继续步入getAllByName方法

java.net.InetAddress.getAllByName()

java.net.InetAddress.getAllByName(java.lang.String)

image.png

java.net.InetAddress.getAllByName(java.lang.String, java.net.InetAddress)

进这个重载方法,参数reqAddr为null

第一个if判断,由于host不为空,跳过

image.png

第二个判断,我们的host开头不是[,跳过ipv6的判断

image.png

第三个判断,根据注释可以了解到如果传入host的是ip地址,就不会进行lookup,也就是dns查询

image.png

addr==null时,进到这个if里面后面的都跳过了

image.png

一直到最后的getAllByName0(),进行域名解析工作

image.png

java.net.InetAddress.getAllByName0(java.lang.String, java.net.InetAddress, boolean)

security返回null

image.png


下面是查找有没有查询缓存,如果有的话会直接返回缓存地址数组

这里我们得让addresses为null,才能步入getAddressFromNameService方法

image.png

跟进getAddressFromNameService方法

image.png

java.net.InetAddress.getAddressesFromNameService

image.png

一开始初始一些变量的值

image.png


image.png

判断host是否在table里面

然后跟进到下面的遍历

跟进lookupAllHostAddr方法

image.png

其实到这一步,已经完成了一次dns查询,这条链子也就到头了

java中实现dns查询的原理

分析URLDNS构造链

调试结束,我们如何构造呢

这里handler调用hashCode

image.png

handler值我们可以在URL对象的构造函数里给

image.png

URLStreamHandler是个抽象类,无法直接实例化,这里yeoserial找到了另一个可以使用的类

SilentURLStreamHandler

image.pngJava中子类会覆盖父类的同名方法,所以我们在子类里重新写了这两个方法,可以避免在ysoserial生成payload的时候发起dns解析造成干扰结果

这样我们可以拿到一个URLStreamHandler实例对象

image.png

让hashCode = -1

上面遗留下来的问题,在java.net.URL.hashCode()中,如果hasnCode不等于-1,链子的下半部分就无法进行

ysoserial中用反射让hashCode强制赋值为-1

image.png

总结

左边构造的payload可以让我们顺着右边的方法层层取出URLStreamHandler类来调用其getHostAddress方法

漏洞最核心的地方在右边调用链的最最下面,我们上面的所有工作都是为了让其能通过反序列化的readObject方法自动执行链条,这一个上一章讲过

所以要一直找利用类,来满足我们:

调用私有方法

让链条可以顺利在我们想要的路上进行,不会被条件搞乱

从最外层开始到内部的核心数据不改变

等等等等(因为还是初学所以暂时总结,马上去找大师傅的心法

image.png

所以首先要调试链子,然后再去分析,分析是建立在调试的基础上的,以后自主审计是另一回事,但是学习过程中先要调试。

分析的时候看着前人挖出来的链子才能知道他为什么找这些类。