java安全(五)java反序列化

这篇具有很好参考价值的文章主要介绍了java安全(五)java反序列化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

给个关注?宝儿!
给个关注?宝儿!
给个关注?宝儿!

1. 序列化

在调用RMI时,发现接收发送数据都是反序列化数据.

例如JSON和XML等语言,在网络上传递信息,都会用到一些格式化数据,大多数处理方法中,JSON和XML支持的数据类型就是基本数据类型,整型、浮点型、字符串、布尔等,如果开发者希望在传输数据的时候直接传输一个对象,那么就不得不想办法扩展基础的JSON(XML)语法。比如,Jackson和Fastjson这类序列化库,在JSON(XML)的基础上进行改造,通过特定的语法来传递对象.

RMI使用java等语言内置的序列化方法,将一个对象转化成一串二进制数据进行传输

2.反序列化

不管是Jackson、Fastjson还是编程语言内置的序列化方法,一旦涉及到序列化与反序列化数据,就可能会涉及到安全问题。但首先要理解的是,“反序列化漏洞”是对一类漏洞的泛指,而不是专指某种反序列化方法导致的漏洞,比如Jackson反序列化漏洞和Java readObject造成的反序列化漏洞就是完全不同的两种漏洞。

在Java中实现对象反序列化非常简单,实现java.io.Serializable(内部序列化)java.io.Externalizable(外部序列化)接口即可被序列化,其中java.io.Externalizable接口只是实现了java.io.Serializable接口。

反序列化类对象时有如下限制:
1.被反序列化的类必须存在。
2. serialVersionUID值必须一致。

除此之外,反序列化类对象是不会调用该类构造方法的,因为在反序列化创建类实例时使用了sun.reflect.ReflectionFactory.newConstructorForSerialization创建了一个反序列化专用的Constructor(反射构造方法对象),使用这个特殊的Constructor可以绕过构造方法创建类实例(前面章节讲sun.misc.Unsafe 的时候我们提到了使用allocateInstance方法也可以实现绕过构造方法创建类实例)。

3.反序列化方法的对比

在接触Java反序列化之前,相比大家多少都了解过其他语言的反序列化漏洞,其中极为经典的要数PHP
和Python。
那么,Java的反序列化,究竟和PHP、Python的反序列化有什么异同?
Java的反序列化和PHP的反序列化其实有点类似,他们都只能将一个对象中的属性按照某种特定的格式
生成一段数据流,在反序列化的时候再按照这个格式将属性拿回来,再赋值给新的对象。
但Java相对PHP序列化更深入的地方在于,其提供了更加高级、灵活地方法 writeObject ,允许开发者
在序列化流中插入一些自定义数据,进而在反序列化的时候能够使用 readObject 进行读取。
当然,PHP中也提供了一个魔术方法叫 __wakeup ,在反序列化的时候进行触发。很多人会认为Java的 readObject 和PHP的 __wakeup 类似,但其实不全对,虽然都是在反序列化的时候触发,但他们解决
的问题稍微有些差异。
Java设计 readObject 的思路和PHP的 __wakeup 不同点在于:
readObject 倾向于解决“反序列化时如
何还原一个完整对象”这个问题,而PHP的 __wakeup 更倾向于解决“反序列化后如何初始化这个对象”的
问题。

4.PHP反序列化

PHP的序列化是开发者不能参与的,开发者调用 serialize 函数后,序列化的数据就已经完成了,你得到的是一个完整的对象,你并不能在序列化数据流里新增某一个内容,你如果想插入新的内容,只有将其保存在一个属性中。也就是说PHP的序列化、反序列化是一个纯内部的过程,而其 __sleep 、 __wakeup 魔术方法的目的就是在序列化、反序列化的前后执行一些操作。

一个非常典型的PHP序列化例子,就是含有资源类型的PHP类,如数据库连接:

<?php 
class Connection 
{ 
protected $link; 
private $dsn, $username, $password; 
public function __construct($dsn, $username, $password) 
{ 
$this->dsn = $dsn; 
$this->username = $username; 
$this->password = $password; 
$this->connect(); 
}
private function connect() 
{ 
$this->link = new PDO($this->dsn, $this->username, $this- 
>password); 
} 
} 

PHP中,资源类型的对象默认是不会写入序列化数据中的。那么上述Connection类的 $link 属性在序
列化后就是null,反序列化时拿到的也是null。
那么,如果我想要反序列化时拿到的 $link 就是一个数据库连接,我就需要编写 __wakeup 方法:

<?php 
class Connection 
{ 
protected $link; 
private $dsn, $username, $password; 
public function __construct($dsn, $username, $password) 
{ 
$this->dsn = $dsn; 
$this->username = $username; 
$this->password = $password; 
$this->connect(); 
}
private function connect() 
{ 
$this->link = new PDO($this->dsn, $this->username, $this- 
>password); 
}
public function __sleep() 
{ 
return array('dsn', 'username', 'password'); 
}
public function __wakeup() 
{ 
$this->connect(); 
} 


可见,这里 __wakeup 的工作就是在反序列化拿到Connection对象后,执行 connect() 函数,连接数
据库。
__wakeup 的作用在反序列化后,执行一些初始化操作。但其实我们很少利用序列化数据传递资源类型
的对象,而其他类型的对象,在反序列化的时候就已经赋予其值了。
所以你会发现,PHP的反序列化漏洞,很少是由 __wakeup 这个方法触发的,通常触发在析构函数
__destruct 里。其实大部分PHP反序列化漏洞,都并不是由反序列化导致的,只是通过反序列化可以
控制对象的属性,进而在后续的代码中进行危险操作。

5.Java反序列化

在Java中实现对象反序列化非常简单,实现java.io.Serializable(内部序列化)java.io.Externalizable(外部序列化)接口即可被序列化,其中java.io.Externalizable接口只是实现了java.io.Serializable接口。

反序列化类对象时有如下限制:
1.被反序列化的类必须存在。
2. serialVersionUID值必须一致。

除此之外,反序列化类对象是不会调用该类构造方法的,因为在反序列化创建类实例时使用了sun.reflect.ReflectionFactory.newConstructorForSerialization创建了一个反序列化专用的Constructor(反射构造方法对象),使用这个特殊的Constructor可以绕过构造方法创建类实例(前面章节讲sun.misc.Unsafe 的时候我们提到了使用allocateInstance方法也可以实现绕过构造方法创建类实例)。

使用反序列化方式创建类实例代码片段:

package com.anbai.sec.serializes;

import sun.reflect.ReflectionFactory;

import java.lang.reflect.Constructor;

/**
 * 使用反序列化方式在不调用类构造方法的情况下创建类实例
 * https://www.iteye.com/topic/850027
 */
public class ReflectionFactoryTest {

    public static void main(String[] args) {
        try {
            // 获取sun.reflect.ReflectionFactory对象
            ReflectionFactory factory = ReflectionFactory.getReflectionFactory();

            // 使用反序列化方式获取DeserializationTest类的构造方法
            Constructor constructor = factory.newConstructorForSerialization(
                    DeserializationTest.class, Object.class.getConstructor()
            );

            // 实例化DeserializationTest对象
            System.out.println(constructor.newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

输出
java安全(五)java反序列化

6.ObjectInputStream、ObjectOutputStream

java.io.ObjectOutputStream类最核心的方法是writeObject方法,即序列化类对象。

java.io.ObjectInputStream类最核心的功能是readObject方法,即反序列化类对象。

所以,只需借助ObjectInputStream和ObjectOutputStream类我们就可以实现类的序列化和反序列化功能了。

7.java.io.Serializable

java.io.Serializable是一个空的接口,我们不需要实现java.io.Serializable的任何方法,代码如下:

public interface Serializable {
}

您可能会好奇我们实现一个空接口有什么意义?其实实现java.io.Serializable接口仅仅只用于标识这个类可序列化。实现了java.io.Serializable接口的类原则上都需要生产一个serialVersionUID常量,反序列化时如果双方的serialVersionUID不一致会导致InvalidClassException 异常。如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID值。

DeserializationTest.java测试代码如下:

package com.anbai.sec.serializes;

import java.io.*;
import java.util.Arrays;

/**
 * Creator: yz
 * Date: 2019/12/15
 */
public class DeserializationTest implements Serializable {



	private String username;

	private String email;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public static void main(String[] args) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		try {
			// 创建DeserializationTest类,并类设置属性值
			DeserializationTest t = new DeserializationTest();
			t.setUsername("yz");
			t.setEmail("admin@.com");

			// 创建Java对象序列化输出流对象
			ObjectOutputStream out = new ObjectOutputStream(baos);

			// 序列化DeserializationTest类
			out.writeObject(t);
			out.flush();
			out.close();

			// 打印DeserializationTest类序列化以后的字节数组,我们可以将其存储到文件中或者通过Socket发送到远程服务地址
			System.out.println("DeserializationTest类序列化后的字节数组:" + Arrays.toString(baos.toByteArray()));

			// 利用DeserializationTest类生成的二进制数组创建二进制输入流对象用于反序列化操作
			ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());

			// 通过反序列化输入流(bais),创建Java对象输入流(ObjectInputStream)对象
			ObjectInputStream in = new ObjectInputStream(bais);

			// 反序列化输入流数据为DeserializationTest对象
			DeserializationTest test = (DeserializationTest) in.readObject();
			System.out.println("用户名:" + test.getUsername() + ",邮箱:" + test.getEmail());

			// 关闭ObjectInputStream输入流
			in.close();
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

}

输出:
java安全(五)java反序列化
核心逻辑其实就是使用ObjectOutputStream类的writeObject方法序列化DeserializationTest类,使用ObjectInputStream类的readObject方法反序列化DeserializationTest类而已。

简化后的代码片段如下:

// 序列化DeserializationTest类
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(t);

// 反序列化输入流数据为DeserializationTest对象
ObjectInputStream in = new ObjectInputStream(bais);
DeserializationTest test = (DeserializationTest) in.readObject();

ObjectOutputStream序列化类对象的主要流程是首先判断序列化的类是否重写了writeObject方法,如果重写了就调用序列化对象自身的writeObject方法序列化,序列化时会先写入类名信息,其次是写入成员变量信息(通过反射获取所有不包含被transient修饰的变量和值)。

8.java.io.Externalizable.java:

public interface Externalizable extends java.io.Serializable {

  void writeExternal(ObjectOutput out) throws IOException;

  void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

}

ExternalizableTest.java测试代码如下:

package com.anbai.sec.serializes;

import java.io.*;
import java.util.Arrays;

/**
 * Creator: yz
 * Date: 2019/12/15
 */
package com.anbai.sec.serializes;

import java.io.*;
import java.util.Arrays;

/**
 * Creator: yz
 * Date: 2019/12/15
 */
public class ExternalizableTest implements java.io.Externalizable {

	private String username;

	private String email;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeObject(username);
		out.writeObject(email);
	}

	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		this.username = (String) in.readObject();
		this.email = (String) in.readObject();
	}

	public static void main(String[] args) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		try {
			// 创建ExternalizableTest类,并类设置属性值
			ExternalizableTest t = new ExternalizableTest();
			t.setUsername("yz");
			t.setEmail("admin@javaweb.org");

			ObjectOutputStream out = new ObjectOutputStream(baos);
			out.writeObject(t);
			out.flush();
			out.close();

			// 打印ExternalizableTest类序列化以后的字节数组,我们可以将其存储到文件中或者通过Socket发送到远程服务地址
			System.out.println("ExternalizableTest类序列化后的字节数组:" + Arrays.toString(baos.toByteArray()));
			System.out.println("ExternalizableTest类反序列化后的字符串:" + new String(baos.toByteArray()));

			// 利用DeserializationTest类生成的二进制数组创建二进制输入流对象用于反序列化操作
			ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());

			// 通过反序列化输入流创建Java对象输入流(ObjectInputStream)对象
			ObjectInputStream in = new ObjectInputStream(bais);

			// 反序列化输入流数据为ExternalizableTest对象
			ExternalizableTest test = (ExternalizableTest) in.readObject();
			System.out.println("用户名:" + test.getUsername() + ",邮箱:" + test.getEmail());

			// 关闭ObjectInputStream输入流
			in.close();
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

}

输出:
java安全(五)java反序列化
两者之间没有多大差别

9.自定义序列化(writeObject)和反序列化(readObject)

实现了java.io.Serializable接口的类,还可以定义如下方法(反序列化魔术方法),这些方法将会在类序列化或反序列化过程中调用:

  1. private void writeObject(ObjectOutputStream oos),自定义序列化。
  2. private void readObject(ObjectInputStream ois),自定义反序列化。
  3. private void readObjectNoData()。
  4. protected Object writeReplace(),写入时替换对象。
  5. protected Object readResolve()。

具体的方法名定义在java.io.ObjectStreamClass#ObjectStreamClass(java.lang.Class<?>),其中方法有详细的声明。

序列化时可自定义的方法示例代码:

public class DeserializationTest implements Serializable {

/**
     * 自定义反序列化类对象
     *
     * @param ois 反序列化输入流对象
     * @throws IOException            IO异常
     * @throws ClassNotFoundException 类未找到异常
     */
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("readObject...");

        // 调用ObjectInputStream默认反序列化方法
        ois.defaultReadObject();

        // 省去调用自定义反序列化逻辑...
    }

    /**
     * 自定义序列化类对象
     *
     * @param oos 序列化输出流对象
     * @throws IOException IO异常
     */
    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();

        System.out.println("writeObject...");
        // 省去调用自定义序列化逻辑...
    }

    private void readObjectNoData() {
        System.out.println("readObjectNoData...");
    }

    /**
     * 写入时替换对象
     *
     * @return 替换后的对象
     */
    protected Object writeReplace() {
        System.out.println("writeReplace....");

        return null;
    }

    protected Object readResolve() {
        System.out.println("readResolve....");

        return null;
    }

}

当我们对DeserializationTest类进行序列化操作时,会自动调用(反射调用)该类的writeObject(ObjectOutputStream oos)方法,对其进行反序列化操作时也会自动调用该类的readObject(ObjectInputStream)方法,也就是说我们可以通过在待序列化或反序列化的类中定义readObject和writeObject方法,来实现自定义的序列化和反序列化操作,当然前提是,被序列化的类必须有此方法,并且方法的修饰符必须是private。文章来源地址https://www.toymoban.com/news/detail-497338.html

到了这里,关于java安全(五)java反序列化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java安全 反序列化(1) URLDNS链原理分析

    开始学习Java反序列化链–URLDNS 请提前了解Java序列化和反序列化,熟悉Java反射机制 1.判断是否存在反序列化的点 2.判断目标是否出网 先上payload 后进行分析 可以触发dns请求 1.新建HashMap类 什么是HashMap: 基于哈希表的实现的 Map 接口 HashMap是什么? 是键值对映射关系的集合,可

    2024年04月09日
    浏览(53)
  • Java安全研究——反序列化漏洞之CC链

    apache commons-collections组件下的反序列化漏洞,自从该组件被爆出漏洞后,许多安全研究员相继挖掘到java多种组件的漏洞,危害严重。本人也是初学Java审计不久,技术薄弱,所以在此做一个cc链的学习总结,如有错误还请大佬指出。 若本文有侵权行为,请立即私信,将全面修

    2024年02月04日
    浏览(49)
  • Java安全之反序列化回显与内存码

    import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; @WebServlet(“/demoServlet”) public class demoServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletEx

    2024年04月15日
    浏览(32)
  • 不安全的反序列化(php&java)及漏洞复现

    A8:2017-不安全的反序列化 A08:2021-Software and Data Integrity Failures 为什么要序列化? 序列化, 将对象的状态信息转换为可以存储或传输的形式的过程 ,这种形式大多为字节流、字符串、json 串。在序列化期间内,将对象当前状态写入到临时或永久性的存储区。以后,就可以通过从

    2024年02月09日
    浏览(47)
  • 【java安全】Log4j反序列化漏洞

    关于Apache Log4j Log4j是Apache的开源项目,可以实现对System.out等打印语句的替代,并且可以结合spring等项目,实现把日志输出到控制台或文件等。而且它还可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码,满足了大多数要求。 就是用来打印日志的 漏洞成因

    2024年02月11日
    浏览(39)
  • 告别脚本小子系列丨JAVA安全(8)——反序列化利用链(下)

    0x01 前言 在前面的文章中介绍了基于CC链的反序列化利用方式,并且通过最终调用Runtime类的exec方法达到命令执行的效果。在CC链中还可以通过xalan来执行命令。 xalan是java操作xml的库,属于java内置的官方库之一,在CC链中主要用到的是com.sun.org.apache.xalan.internal.xsltc.trax.Templates

    2024年02月11日
    浏览(40)
  • 【java安全】原生反序列化利用链JDK7u21

    前言 前面我们学习了使用第三方类: Common-Collections 、 Common-Beanutils 进行反序列化利用。我们肯定会想,如果不利用第三方类库,能否进行反序列化利用链呢?这里还真有:JDK7u21。但是只适用于java 7u及以前的版本 在使用这条利用链时,需要设置jdk为jdk7u21 原理 JDK7u21这条链利

    2024年02月14日
    浏览(34)
  • 小迪安全47WEB 攻防-通用漏洞&Java 反序列化&EXP 生成&数据提取&组件安全

    # 知识点: 1 、 Java 反序列化演示 - 原生 API 接口 2 、 Java 反序列化漏洞利用 -Ysoserial 使用 3 、 Java 反序列化漏洞发现利用点 - 函数 数据 4 、 Java 反序列化考点 - 真实 CTF 赛题 - 审计分析 # 内容点: 1 、明白 -Java 反序列化原理 2 、判断 -Java 反序列化漏洞 3 、学会 -Ysoserial 工具

    2024年04月10日
    浏览(53)
  • WEB攻防-Java安全&原生反序列化&SpringBoot攻防&heapdump提取&CVE

    知识点: 1、Java安全-原生反序列化-3大类接口函数利用 2、Java安全-SpringBoot攻防-泄漏安全CVE安全 序列化是将Java对象转换成字节流的过程。而反序列化是将字节流转换成Java对象的过程,java序列化的数据一般会以标记( ac ed 00 05 )开头,base64编码的特征为 rO0AB 。 JAVA常见的序列化

    2024年02月03日
    浏览(35)
  • 【java安全】无Commons-Collections的Shiro550反序列化利用

    Shiro550利用的难点 前面我们学习Shiro反序列化漏洞时,使用了一个简单的shiroDemo,在这个Demo中引入了一些依赖: shiro-core、shiro-web,这是shiro本身的依赖 javax.servlet-api、jsp-api,这是JSP和Servlet的依赖,仅在编译阶段使用,因为Tomcat中自带这 两个依赖 slf4j-api、slf4j-simple,这是为

    2024年02月13日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包