PHP反序列化漏洞總結(一)


寫在前邊

  做了不少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

 

如有錯誤還請指出,謝謝

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM