作了很多PHP反序列化的題了,是時候把坑給填上了。參考了一些大佬們的博客,本身再作一下總結php
在瞭解序列化和反序列化以前,先簡單瞭解一下PHP的面向對象。html
萬物皆可對象。根據官方手冊,PHP中,以關鍵字class定義一個類,一個類能夠包含有屬於本身的常量,變量(稱爲「屬性」)以及函數(稱爲「方法」)。web
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(); ?>
根據官方手冊,全部php裏面的值均可以使用函數serialize()來返回一個包含字節流的字符串來表示。unserialize()函數可以從新把字符串變回php原來的值。 序列化一個對象將會保存對象的全部變量,可是不會保存對象的方法,只會保存類的名字。emmmm。。。。我的理解其實就是爲了解決PHP在執行當前腳本須要跨腳本文件傳遞某些變量內容時,但由於以前腳本執行完後把內容釋放掉從而沒法獲取的問題。serialize能夠將變量轉換爲字符串,而且在轉換的過程當中能夠保存當前變量的值,而unserialize則能夠將serialize生成的字符串轉換回變量。函數
根據以前的例子,再添加個age屬性ui
<?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類型this
而後反序列化這段,新建一個文件test2.phpspa
<?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();
?>
效果:code
其實吧,我的感受序列化和反序列化就相似於存取數組。舉個不恰當的例子,就像積木,序列化一個把搭建好後的積木收拾好,反序列化就是把收拾好的積木再按照原來的圖紙搭起來。接下來講到的反序列化漏洞就相似於把原來的積木換了個顏色,某塊積木形狀對的但顏色不對,按照圖紙搭起來就是感受違和。htm
瞭解了什麼是序列化和反序列化後,是時候研究一下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
若有錯誤還請指出,謝謝