返回
顶部

前言

100岁生日快乐!!!

download

参考链接:

正文

PHP的反序列化函数unserialize()接受两个参数

  • 序列化字符串
  • 选项

对于PHP开发者来说,选项只有一个,要么不设置,设置的话就只能是allowed_classes

定义了allowed_classes选项之后,只有被PHP允许的类才能够被反序列化,否则会被初始化为__PHP_Incomplete_Class

反序列化流程:

image-20210630165947906

也就是说,只要我们的反序列化类实现了__wakeup或者__destruct中的任何一个方法,只要serialized方法反序列化了这样的对象,这两个方法就会被执行

反序列化一个对象需要满足一个前提

  • 这个类一定是被预先定义过的

如果不满足这个前提,那么反序列化出来的类就是会被初始化为__PHP_Incomplete_Class,但是其类名和成员变量还是可以正常反序列化出来的,只是PHP认为他是一个没有被定义的类

php的serialized方法会保存被序列化的对象的所有属性(成员变量),有了这个,unserialize就能创建出一个原来的对象的copy

copy创建出来之后,PHP会搜索该对象的__wakeup方法,如果找到了该方法,就会执行其中的代码,该方法一般用来恢复数据库连接,因为序列化的对象在传输过程中可能已经和数据库失去了连接,需要在该方法中编写代码来重新初始化连接

然后就是对对象进行操作(调用对象的方法),最后销毁对象(调用__destruct方法)

一旦攻击者控制类传递给unserialize方法的序列化字符串,那么攻击者就能够控制对象的属性(成员变量)

能够随心所欲的操作成员变量,就有可能控制程序执行的流程,进而达到RCE

利用反序列化漏洞需要两个前提:

  • 反序列化的类一定要被预先定义
  • 该类一定要拥有魔术方法__wakeup或者__destruct,不管最终存不存在漏洞,如果没有这两个方法,你连注入代码的机会都没有

例子

假如有下面这样一段代码:

<?php
class Fuck
{
    private $test;

    function __destruct()
    {
        system($this->test);
    }
}
$obj = unserialize(base64_decode(此处接收用户输入));

那么我们对应的exp就是:

<?php

class Fuck
{
    private $test = "hostname";
}

print(base64_encode(serialize(new Fuck)));

image-20210630191018877

预防反序列化漏洞

限制反序列化或者白名单

//不允许反序列化对象(初始化出一个__PHP_Incomplete_Class对象),但是你仍然可以反序列化其他的数据结构,比如数组等
$object = unserialize($data, ['allowed_classes' => false]);

//白名单,只能反序列化白名单中的类
$whitelist = ['MyProject\\OtherNamespace\\ObjectAllowed'];
$object = unserialize($data, ['allowed_classes' => $whitelist]);

建议不要将用户输入用于反序列化