PHP反序列化漏洞總結

一條沒有夢想的鹹魚TvT發表於2020-04-24

寫在前邊

  做了不少PHP反序列化的題了,是時候把坑給填上了。參考了一些大佬們的部落格,自己再做一下總結

1.物件導向
2.PHP序列化和反序列化
3.PHP反序列化漏洞例項

1.物件導向

  在瞭解序列化和反序列化之前,先簡單瞭解一下PHP的物件導向。

  萬物皆可物件。根據官方手冊,PHP中,以關鍵字class定義一個類,一個類可以包含有屬於自己的常量,變數(稱為“屬性”)以及函式(稱為“方法”)。

class People
{
    //宣告屬性
    public $name;
    //宣告方法
    public function eat()
    {
        echo $this->name."在吃飯";
    }
}

  現在已經定義好了一個類,接下來用關鍵字new來例項化這個類

$people_1 = new People();  //例項化物件
$people_1->name = '張三';  //屬性賦值
$people_1->Eat();      //呼叫方法

  完整程式碼

<?php

class People
{
    public $name;
    public function eat()
    {
        echo $this->name."在吃飯";
    }
}

$people_1 = new People();
$people_1->name = '張三';
$people_1->Eat();

?>

  效果:

 

2.PHP序列化和反序列化

  根據官方手冊,所有php裡面的值都可以使用函式serialize()來返回一個包含位元組流的字串來表示。unserialize()函式能夠重新把字串變回php原來的值。 序列化一個物件將會儲存物件的所有變數,但是不會儲存物件的方法,只會儲存類的名字。emmmm。。。。個人理解其實就是為了解決PHP在執行當前指令碼需要跨指令碼檔案傳遞某些變數內容時,但因為之前指令碼執行完後把內容釋放掉從而無法獲取的問題。serialize可以將變數轉換為字串,並且在轉換的過程中可以儲存當前變數的值,而unserialize則可以將serialize生成的字串轉換回變數。

根據之前的例子,再新增個age屬性

<?php

class People
{
    public $name;
    public $age;   //新加屬性 
    public function Eat()
    {
        echo $this->name."在吃飯</br>";
    }
}

$people_1 = new People();
$people_1->name = '張三';
$people_1->age = 18;
$people_1->Eat();  

//序列化
echo serialize($people_1);
?>

  效果:

 

  O:6:"People":2:{s:4:"name";s:6:"張三";s:3:"age";i:18;}就是當前people_1這個物件序列化後的形式。"O"表示物件,“6”表示物件所屬的類長度為6,“People”為類名,“2”表示有2個引數。“{}”裡面是引數的key和value,s:4:"name"表示這個引數的string型別,長度為4,key值是name。後面以此類推,i表示int型別

  然後反序列化這段,新建一個檔案test2.php

<?php

class People
{
    public $name;
    public $age;
    public function eat()
    {
        echo $this->name."在吃飯</br>";
    }
}

//重建物件
$usr = unserialize('O:6:"People":2:{s:4:"name";s:6:"張三";s:3:"age";i:18;}');

//輸出
$usr->eat();
?>

  效果:

 

 

   其實吧,個人感覺序列化和反序列化就類似於存取陣列。舉個不恰當的例子,就像積木,序列化一個把搭建好後的積木收拾好,反序列化就是把收拾好的積木再按照原來的圖紙搭起來。接下來說到的反序列化漏洞就類似於把原來的積木換了個顏色,某塊積木形狀對的但顏色不對,按照圖紙搭起來就是感覺違和。

 

3.PHP反序列化漏洞

  瞭解了什麼是序列化和反序列化後,是時候研究一下PHP反序列化漏洞了,實際上,PHP反序列化漏洞利用的條件在實際環境中比較苛刻,但是如果可以利用一般都會產生很嚴重的後果

  該漏洞需要有以下條件: 

    1.unserialize函式的引數可控

    2.所寫的內容需要有物件中的成員變數的值

    3.指令碼中存在一個建構函式(__construct())、解構函式(__destruct())、__wakeup()函式中有向php檔案中寫資料的操作的類

    __construct()當一個物件建立時被呼叫

    __destruct()當一個物件銷燬時被呼叫

    __toString()當一個物件被當作一個字串使用

    __sleep() 在物件在被序列化之前執行

    __wakeup將在序列化之後立即被呼叫所寫的內容需要有物件中的成員變數的值

  這裡先借大佬的舉個的例子(後續再填坑)

  建立test3.php

<?php 
class Test{
    var $test = "123";
    function __wakeup(){
        $fp = fopen("test.php", 'w');
        fwrite($fp, $this -> test);
        fclose($fp);
    }
}

$test1 = $_GET['test'];
print_r($test1);
echo "<br />";
$seri = unserialize($test1);

require "test.php";

?>

  先盲目分析一波,Test這類有個重寫的__wakeup()這個魔術方法,當序列化後,開啟test.php,許可權為寫,將$test值重寫到test.php,用GET方式將值傳入$test1,然後反序列化。就是我們如果傳入一個序列化後的EXP傳入,text.php就會變成我們出傳入內容

      當值為空時,訪問http://本地環境/test3.php?test=  

  根據剛剛的方法,新建一個指令碼,序列化一下

<?php 
class Test{
    var $test = "123";
    function __wakeup(){
        $fp = fopen("test.php", 'w');
        fwrite($fp, $this -> test);
        fclose($fp);
    }
}

$test = new Test();
echo serialize($test);

?>

  然後我們跟上引數 O:4:"Test":1:{s:4:"test";s:3:"123";}

  發現不但頁面更改了而且連原檔案都被重寫了,這時一個PHP反序列化漏洞就產生了

  (目前只是簡單總結,實戰方面先挖個坑,後續再補)

 

參考

https://www.freebuf.com/articles/web/167721.html

https://www.jianshu.com/p/be6de8511cb9

 

如有錯誤還請指出,謝謝

 

相關文章