相关文章推荐
慷慨的抽屉  ·  遭遇ERROR ssl: ...·  6 天前    · 
睿智的松鼠  ·  虚拟数字人开源-阿里云·  6 天前    · 
聪明伶俐的围巾  ·  fp = ...·  4 天前    · 
坏坏的莴苣  ·  干货合集 - 知乎·  1 年前    · 
坚韧的红薯  ·  the json value could ...·  1 年前    · 
沉着的红烧肉  ·  Python argparse ...·  1 年前    · 
php(phar)反序列化漏洞及各种绕过姿势

php(phar)反序列化漏洞及各种绕过姿势

本文为看雪论坛优秀文章

看雪论坛作者ID:pank1s



一、简介


序列化其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化。简单来说就是我在一个地方构造了一个类,但我要在另一个地方去使用它,那怎么传过去呢?于是就想到了序列化这种东西,将对象先序列化为一个字符串(数据),后续需要使用的时候再进行反序列化即可得到要使用的对象,十分方便。


来看看官方手册( php.net/manual/zh/langu )怎么说:


所有php里面的值都可以使用函数serialize()( php.net/manual/zh/funct )来返回一个包含字节流的字符串来表示。unserialize()( php.net/manual/zh/funct )函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。


为了能够unserialize()一个对象,这个对象的类必须已经定义过。如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。 如果要想在另外一个文件中反序列化一个对象,这个对象的类必须在反序列化之前定义,可以通过包含一个定义该类的文件或使用函数spl_autoload_register()( php.net/manual/zh/funct )来实现。


php 将数据序列化和反序列化会用到两个函数:

serialize() 将对象格式化成有序的字符串。unserialize() 将字符串还原成原来的对象。


序列化的目的是方便数据的传输和存储,在PHP中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。


注意:php中创建一个对象和反序列化得到一个对象是有所不同的,例如创建一个对象一般会优先调用 __construct() 方法 ,而反序列化得到一个对象若 存在 __wakeup() 方法则会优先调用它而不去执行 __construct() 。



二、常见序列化格式介绍


基本上每个编程语言都有各自的序列化和反序列化方式,格式也各不相同

像有:


简单例子

<?php
$arr = array('aa', 'bb', 'cc' => 'dd');
$serarr = serialize($arr);
echo $serarr;
var_dump($arr);


输出

a:3:{i:0;s:2:"aa";i:1;s:2:"bb";s:2:"cc";s:2:"dd";}
array(3) {
    [0]=> string(2) "aa"
    [1]=> string(2) "bb"
    ["cc"]=> string(2) "dd"
}


输出的这一串序列表示的是什么呢?a:3:{i:0;s:2:"aa";i:1;s:2:"bb";s:2:"cc";s:2:"dd";}a:array代表是数组,后面的3说明有三个属性。i:代表是整型数据int,后面的0是数组下标(O代表Object,也是类)。s:代表是字符串,后面的2是因为aa长度为2,是字符串长度值。后面类推。

同时要注意序列化后只有成员变量,没有成员函数。


注意如果变量前是protected,则会在变量名前加上\x00*\x00,private则会在变量名前加上\x00类名\x00,输出时一般需要url编码,如下:

<?php
class test {
    protected $name;
    private $pass;
    function __construct($name, $pass) {
        $this->name = $name;
        $this->pass = $pass;
$a = new test('pankas', '123');
$seria = serialize($a);
echo $seria.'<br/>';
echo urlencode($seria);


直接输出输出则会导致不可见字符\x00的丢失。

O:4:"test":2:{s:7:"*name";s:6:"pankas";s:10:"testpass";s:3:"123";}
O%3A4%3A%22test%22%3A2%3A%7Bs%3A7%3A%22%00%2A%00name%22%3Bs%3A6%3A%22pankas%22%3Bs%3A10%3A%22%00test%00pass%22%3Bs%3A3%3A%22123%22%3B%7D



三、反序列化常用魔术方法


详细用法请参考 官方文档( php.net/manual/zh/langu

__construct()//类的构造函数,创建类对象时调用
__destruct()//类的析构函数,对象销毁时调用
__call()//在对象中调用一个不可访问方法时调用
__callStatic()//用静态方式中调用一个不可访问方法时调用
__get()//获得一个类的成员变量时调用
__set()//设置一个类的成员变量时调用
__isset()//当对不可访问属性调用isset()或empty()时调用
__unset()//当对不可访问属性调用unset()时被调用。
__sleep()//执行serialize()时,先会调用这个函数
__wakeup()//执行unserialize()时,先会调用这个函数,执行后不会执行__construct()函数
__toString()//类被当成字符串时的回应方法
__invoke()//调用函数的方式调用一个对象时的回应方法
__set_state()//调用var_export()导出类时,此静态方法会被调用。