前言

给java反序列化上个引子,顺带把webgoat代码审计结束了

本来要开始学链子了,但是网警和技侦要开始考试了,打算突击一下ctf和代码审计,下个月在正式学习java反序列化

Java 流(Stream)、文件(File)和IO | 菜鸟教程 (runoob.com)

序列化和反序列化

Java 序列化是一种将对象转换为字节流的过程,以便可以将对象保存到磁盘上,将其传输到网络上,或者将其存储在内存中,以后再进行反序列化,将字节流重新转换为对象。

序列化在 Java 中是通过 java.io.Serializable 接口来实现的,该接口没有任何方法,只是一个标记接口,用于标识类可以被序列化。

当你序列化对象时,你把它包装成一个特殊文件,可以保存、传输或存储。反序列化则是打开这个文件,读取序列化的数据,然后将其还原为对象,以便在程序中使用。

序列化是一种用于保存、传输和还原对象的方法,它使得对象可以在不同的计算机之间移动和共享,这对于分布式系统、数据存储和跨平台通信非常有用。

Java 流(Stream)、文件(File)和IO

Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。

Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。

一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。

Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。

img

java 反射

java反射机制深入理解剖析_w3cschool

在java开发中有一个非常重要的概念就是java反射机制,也是java的重要特征之一。反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力,通过反射可以调用私有方法和私有属性,大部分框架也都是运用反射原理的。java通常是先有类再有对象,有对象就可以调用方法或者属性,java中的反射其实是通过Class对象来调用类里面的方法。掌握了反射的知识,才能更好的学习java高级课程。

一个类有多个组成部分,例如:成员变量、方法、构造方法等,反射就是加载类,并解剖出类的各个组成部分。

image-20231127110009212

序列化过程简单演示

先创建一个类Person.java

package test;

import java.io.Serializable;

public class Person implements Serializable {
    private String username;
    public int age;

    public Person(String username, int age) {
        this.username = username;
        this.age = age;
    }

    @Override
    public String toString() {
        return "person{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

image-20231127151420206

序列化

序列化文件 SerializationTest.java

package test;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializationTest {
    public static void serialize(Object object) throws IOException{
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream("ser.bin"));
        objectOutputStream.writeObject(object);
    }

    public static void main(String[] args) throws Exception{
        Person person = new Person("water3",23);
        serialize(person);
        System.out.println("serialize:"+person);
    }
}

序列化对象: 使用 ObjectOutputStream 类来将对象序列化为字节流

这里新创建了一个FileOutputStream类来接受序列化对象,该类用来创建一个文件并向文件中写数据。

如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。

有两个构造方法可以用来创建 FileOutputStream 对象。

使用字符串类型的文件名来创建一个输出流对象:

OutputStream f = new FileOutputStream("C:/java/hello")

也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象:

File f = new File("C:/java/hello"); OutputStream fOut = new FileOutputStream(f);

反序列化

package test;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;

public class UnserializationTest {
    public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("ser.bin"));
        Object object = objectInputStream.readObject();
        return object;
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = (Person) unserialize("ser.bin");
        System.out.println("unserialize"+person);
    }
}

这里也可以提前创建个空person对象来接收反序列化后的person对象

image-20231127151513258

Webgoat 反序列化

这道题一直不成功,但还是审计一下:

image-20231127163148626

首先对传入的字符串进行解码,然后进行反序列化,用o接受反序列化之后的对象,最后判断是否为VulnerableTaskHolder的实例

image-20231127163335284

最下面是判断执行时间是否在3到7秒

我们再看一看这个危险类:

image-20231127163555555

这里和例题一样,使用taskAction来rce

image-20231127163838608

从自定义的序列化过程来看,对象必须是十分钟之内创建的,然后rce必须以ping或者sleep开头

payload应该是这样:

image-20231127165418906

但是根据回显在时间判断出了点问题,后来想起来在之前做题的时候发现docker的时间和我的系统时间是对不上的