详解PHP反序列化漏洞示例与原理
预备知识
PHP序列化与反序列化
序列化:将一个复杂的数据类型(如对象、数组、变量等)转换为字符串表示,以便于在网络中传输和在数据库中存储。在PHP语言中使用serialize()
函数实现。
反序列化:将一个序列化的字符串重新转换为一个具体的数据类型。在PHP语言中使用unserialize()
函数实现。 PHP对象中只有数据会被序列化,方法不会被序列化。
序列化字符串格式
PHP中经过序列化的字符串格式如下:
类型:类名长度:“类名”:类属性数量:{属性类型:属性长度:“属性内容”}
序列化字符串中的属性命名规则:如果变量为public,则值保持不变;如果变量为private,则在值开头加上类名前缀;如果变量为protected,则在值开头加*
符号。
下面给出序列化字符串中的属性类型:
类型 | 解释 |
---|---|
s:length:“value” | 字符串 |
i:value | 整数值 |
d:value | 浮点数值 |
b:value | 布尔值 |
a:length:{…} | 数组 |
O:length:“name”:number:{…} | 对象 |
N | NULL |
PHP魔术方法
简介:魔术方法是一种以2个下划线开头的特殊方法,对一个对象执行魔术方法时会覆盖PHP默认的操作。
常见的PHP魔术方法如下:
方法 | 调用时间 |
---|---|
__construct(构造函数) | 创建新对象时 |
__destruct (析构函数) | 销毁对象时 |
__call | 在对象中调用不可用方法时 |
__callStatic | 在上下文中调用不可用方法时 |
__get | 读不可用值时 |
__set | 写不可用值时 |
__isset | 对不可用值调用isset() 或empty() 时 |
__unset | 对不可用值调用unset() 时 |
__sleep | 序列化对象时 |
__wakeup | 反序列化对象时 |
__toString | 对象被作为字符串使用时 |
__invoke | 尝试以调用函数方式调用对象时 |
示例
<?php
class Test{
public $a="aaa";
private $b="bbb";
protected $c=123;
function __construct($a,$b,$c)
{
$this->a=$a;
$this->b=$b;
$this->c=$c;
echo("Constructor called.<br>");
}
function __destruct()
{
echo("Destructor called.<br>");
}
function show()
{
echo($this->a."\n".$this->b."\n".$this->c."<br>");
}
}
$data=new Test("a",1,1.1);
$data_str=serialize($data);
echo($data_str."\n");
$data_new=unserialize($data_str);
echo("<br>");
$data_new->show();
?>
反序列化漏洞
反序列化漏洞指网站未对被用户所控制的序列化字符串做检查,攻击者提交了精心构造的有害序列化字符串,导致恶意代码被执行。常见于PHP、python、Java等允许对象序列化功能的编程语言中。
在PHP中,导致反序列化漏洞的原因大多是魔术方法的不规范使用。(如输出变量内容、写文件操作、写数据库操作等参数用户可控)
构造函数&析构函数
假设网站页面部分代码编写如下:
<?php
class Test
{
var $a="aaa";
function __construct($a)
{
$this->a=$a;
echo("Info:<br>".$a);
}
}
$data_new=unserialize($_GET['data']);
var_dump($data_new);
?>
当对象创建(或销毁)时,程序会输出对象中三个成员变量的值;程序还从api接口读取了一个序列化字符串并试图将其反序列化。此时如果将序列化字符串中的值修改为恶意代码,类名修改为Test
,会导致反序列化漏洞攻击。
EXP:O:4:“Test”:1:{s:4:“test”;s:29:“<script>alert(‘xss’)</script>”;}
CVE-2016-7124
在PHP5 <5.6.25
、PHP7 <7.0.10
的环境中,如果类中存在__wakeup
魔术方法,则在反序列化之前会先调用该方法。但当序列化字符串中属性数量大于真实属性数量时,该方法不会执行。
<?php
class Test
{
var $mess="111";
function __wakeup()
{
$this->mess="failed";
echo("Please try again.<br>");
}
}
$new_data=unserialize($_GET['ins']);
var_dump($new_data);
?>
代码中,当反序列化一个字符串时,程序会执行__wakeup
方法,将$mess
设置为字符串,并显示失败信息。此时把序列化字符串中的属性数量值改大,__wakeup
方法会失效。
EXP:Http://127.0.0.1/serialize.php?ins=O:4:“Test”:2:{s:4:“mess”;s:25:"
<script>alert(1)</script>";}
到此这篇关于详解PHP反序列化漏洞示例与原理的文章就介绍到这了,更多相关PHP反序列化漏洞内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
相关文章