SpringKill
- 关注
前面一篇文章我们详细说了说反序列化的反射部分,这一部分很基础,也就是我们后面想要编写POC等内容的时候经常用来调用一些方法所使用的操作,这一篇我们就正式开始,详细说说反序列化漏洞吧。
序列化和反序列化
那么说到反序列化漏洞,首先肯定要知道什么是反序列化。
序列化和反序列化说到底还是为了通信存在的,同时反序列化也不是Java特有的,比如我们最经常使用和看到的的JSON其实就是一种序列化数据。
序列化和反序列化是一个思想:通过将数据以某种格式/语法进行一个“打包”成一个序列化的数据,然后再由接收端使用固定的格式/语法将其“解包”,从而达到复杂数据的精简传输,这种“打包”生成的内容有可能像JSON等内容一样,是一个特定格式的字符串,也有可能是像Java中的一种二进制流,这取决于对方如何实现。
就单独说说Java以及其中的反序列化吧。
序列化和反序列化的方法
Java原生序列化:
Java原生序列化是Java标准库提供的一种对象序列化方式。它涉及两个主要类:ObjectOutputStream
用于将Java对象序列化为字节流,ObjectInputStream
用于将字节流反序列化为Java对象。要使一个类可序列化,只需实现Serializable
接口,该接口是一个标记接口,不包含任何方法。例如:
import java.io.*;
public class MyClass implements Serializable {
static int value = 100;
static String name = "TestClass";
MyClass() {
System.out.println(name + value);
}
}
然后可以使用ObjectOutputStream
将MyClass
的对象序列化到文件或网络流:
MyClass myObject = new MyClass();
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.bin"))) {
oos.writeObject(myObject);
}
反序列化时,可以使用ObjectInputStream
:
MyClass deserializedObject;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.bin"))) {
deserializedObject = (MyClass) ois.readObject();
}
JSON序列化:
JSON是一种轻量级数据交换格式,常用于Web应用程序的数据传输。Java中有许多开源库(如Jackson、Gson、JSON-B等)可以将Java对象序列化为JSON格式,以及从JSON格式反序列化为Java对象。
以Jackson为例,将Java对象序列化为JSON字符串:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(myObject);
反序列化:
MyClass deserializedObject = objectMapper.readValue(json, MyClass.class);
XML序列化:
XML(eXtensible Markup Language)是另一种用于数据交换的通用格式,与JSON类似,Java中也有库可以实现XML序列化和反序列化。常用的库包括JAXB(Java Architecture for XML Binding)。
以JAXB为例,将Java对象序列化为XML字符串:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
JAXBContext context = JAXBContext.newInstance(MyClass.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter writer = new StringWriter();
marshaller.marshal(myObject, writer);
String xml = writer.toString();
反序列化:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
JAXBContext context = JAXBContext.newInstance(MyClass.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
String xml = "<myClass><value>42</value><name>John</name></myClass>";
MyClass deserializedObject = (MyClass) unmarshaller.unmarshal(new StringReader(xml));
第三方序列化库:
除了JSON和XML,还有一些其他的第三方序列化库,如Apache Avro、Google Protocol Buffers等。这些库通常提供更高效的序列化和反序列化性能,但是同样的也存在一些新的安全风险。
为什么会出现漏洞
其他语言的反序列化漏洞
这一切还要从Java的语言特性说起了,比起其他语言的序列化反序列化,Java能够更方便地修改你想要序列化的流数据(writeObject())
,做过CTF题目的师傅们应该清楚PHP的反序列化,通常在实现反序列化的过程中我们控制的并不是具体的某个对象,而是某个对象的值。
且看如下php代码:
<?php
class phone
{
public $apple;
public $samsung;
public function see()
{
echo eval($this->apple);
}
}
class tv
{
public $skyworth;
public $sony;
public function __toString()
{
$this->skyworth->see();
return "1";
}
}
class book
{
public $math;
public $chinese;
public function read()
{
$this->chinese->learn();
}
}
class bottle
{
public $locklock;
public $fuguang;
public function __toString()
{
$this->locklock->read();
return "lock lock read!";
}
}
class car
{
public $volkswagen;
public $audi;
public function __invoke()
{
$this->volkswagen = $this->audi." Deutsch";
}
}
class fruit
{
public $apple;
public $orange;
public function __call($t1,$t2)
{
$s1 = $this->orange;
$s1();
}
}
?>
想必阅读完代码,构造完链子的时候师傅们就应该明白了,PHP反序列化和Java反序列化漏洞的不同点就在于用户能不能对序列化的数据进行一些“特殊”的比如序列化自定义类这种操作。PHP的反序列化我们常常通过阅读源码后找到危险的点,然后控制类中的属性值来达到目的(其实通常存在于析构函数中),但是Java中我们可以传入任意类来构造恶意数据。
再直白点,如果PHP的系统中不存在能够执行命令的地方,那么我们反序列化的数据也不能,但是Java中却可以,这也是Java反序列化存在的一个重要原因。
那么下一篇我们就来分析具体的反序列化历史漏洞还有著名的CC链吧。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
