PHP反序列化漏洞

一切伟大的行动和思想,都有一个微不足道的开始。 ——阿尔贝·加缪《西西佛神话》

0x00 前言

听说面试经常问PHP反序列化漏洞,想起来我自己也不懂。因为之前都是在搞一些老旧的东西,不能不进步,也差不多要学习高阶的知识了。

0x01 什么是反序列化

明白序列化,就只知道什么是反序列化了,我觉得可以这样理解:

将数据或对象转换成另外一种利于存储或运输的格式,且这个转换过程是可逆的,这个过程就可以称为序列化,而逆向转换就是反序列化。

除了PHP存在反序列化漏洞外,Java也存在反序列化漏洞,Java反序列化漏洞的分析后面慢慢更新吧。
PHP手册中关于对象序列化的描述:

所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示,unserialize()函数能够重新把字符串变回php原来的值。
反序列化一个对象将会保存对象的所有变量。但是不会保存对象的方法,只会保存类的名字。为了能够unserialize()一个对象,这个对象的类必须已经定义过。
如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。如果要想在另外一个文件中解序列化一个对象,这个对象的类必须在解序列化之前定义,可以通过包含一个该类的文件或使用函数spl_autoload_register()来实现。

在PHP应用中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。
常见的序列化格式:

二进制格式
字节数组
json字符串
xml字符串 

0x02 反序列化漏洞原理及其复现

PHP中序列化和反序列化对应的函数就是serialize()和unserialize()。如果反序列化函数中的参数能被用户控制,就会产生反序列化漏洞。

PHP中存在一些以符号__开头的magic函数(方法),magic函数是在某些条件下自动执行的函数。魔术方法是在实现一些功能,但是一般代码不能实现或者很难实现的时候才用。以下是PHP类中一些magic函数及其用法:

__construct()        当一个对象创建时触发 *
__destruct()         当一个对象被销毁时触发 *
__toString()         把类当做字符串处理时触发 *
__call()             在对象上下文调用不可访问的方法时触发
__callStatic()       在静态上下文中调用不可访问的方法时触发
__get()              用于从不可访问的属性读取数据时
__set()              用于将数据写入不可访问的属性
__wakeup()           使用unserialize触发 *
__sleep()            使用serialize触发 *

漏洞复现

1
2
3
4
5
6
7
8
9
10
11
12
13
//demo.php
<?php
class A{
public $target = "demo";
function __destruct(){
echo "destructing!<br/>";
echo $this->target."<br/>";
echo "destructed!<br/>";
}
}
$a = $_GET['test'];
$a_user = unserialize($a);
?>

demo.php中的析构函数会回显$test的值,我们可以构造一个对象,控制$test的值,达到控制数据流的目的,实现反序列化漏洞的利用。
注意:margic_quotes_gpc和magic_quotes_runtime配置项的设置会影响传递到unserialize()中的数据。

执行XSS脚本

1
2
3
4
5
6
7
<?php
class A{
public $targrt = "dfsadfwefafas";
}
$a = serialize(new A);
echo $a;
?>
__wakeup()绕过

(CVE-2016-7124):反序列化时,如果对象属性个数的值大于真实的属性个数时就会跳过__wakeup()的执行,此漏洞影响的主要版本为PHP5 5.6.25以前,PHP7 7.0.1以前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class A{
var $target = "test";
function __wakeup(){
$this->target = "wakeup!";
}

function __destruct(){
$fp = fopen("C:\\phpStudy\\PHPTutorial\\WWW\\temp\\hello.php","w");
fputs($fp,$this->target);
fclose($fp);
}
}

$test = $_GET['test'];
test_unser = unserialize($test);

echo "hello.php<br/>";
include (".\hello.php");
?>

可以看到__wakeup()函数把test的值重置为”wakeup!”。

我们更改序列化中的属性个数为3,发现__wakeup()函数没有执行了。

0x03 总结

php反序列化漏洞的重点在于参数能不能可控,如果可控就可能存在反序列化漏洞。这里只是暂时对原理进行了简单分析,php漏洞如何挖掘可以参考这篇文章。


参考文献:
PHP反序列化漏洞学习总结
如果你在准备面试,这个知识点一定要get!

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×