序列化與反序列化了解
- serialize ()
serialize() 返回字符串蝌诡,此字符串包含了表示 value 的字節(jié)流溉贿,可以存儲(chǔ)于任何地方。
簡單來講浦旱,就是將對(duì)象轉(zhuǎn)化為可以傳輸?shù)淖址钌址写鎯?chǔ)著對(duì)象的變量、類型等颁湖。
舉個(gè)例子:
test.php
<?php
class test{
public $name = "wcute";
public $age = "18";
}
$fairy = new test();
echo serialize($fairy);
?>
- unserialize ()
將序列化后的字符串轉(zhuǎn)化為PHP的值宣蠕。
test.php
<?php
class test{
public $name = "wcute";
public $age = "18";
}
$fairy = new test();
$s_fairy = serialize($fairy);
$uns_fairy = unserialize($s_fairy);
var_dump($uns_fairy); # 打印對(duì)象
?>
魔術(shù)方法
PHP 將所有以 __(兩個(gè)下劃線)開頭的類方法保留為魔術(shù)方法。所以在定義類方法時(shí)甥捺,除了上述魔術(shù)方法抢蚀,建議不要以 __ 為前綴。
__construct()
镰禾,__destruct()
皿曲,__call()
,__callStatic()
吴侦,__get()
屋休, __set()
, __isset()
妈倔,__unset()
博投,__sleep()
,__wakeup()
盯蝴,__toString()
,__invoke()
听怕,__set_state()
捧挺,__clone()
和__debugInfo()
等方法在 PHP 中被稱為"魔術(shù)方法"
(Magic methods)。
常用魔術(shù)方法舉例:
__construct()
PHP 5 允行開發(fā)者在一個(gè)類中定義一個(gè)方法作為構(gòu)造函數(shù)尿瞭。具有構(gòu)造函數(shù)的類會(huì)在每次創(chuàng)建新對(duì)象時(shí)先調(diào)用
此方法闽烙,所以非常適合在使用對(duì)象之前做一些初始化工作。__destruct()
析構(gòu)函數(shù)會(huì)在到某個(gè)對(duì)象
的所有引用都被刪除或
者當(dāng)對(duì)象被顯式銷毀時(shí)執(zhí)行
声搁。__sleep()
serialize() 函數(shù)會(huì)檢查類中是否存在一個(gè)魔術(shù)方法 __sleep()黑竞。如果存在,該方法會(huì)先被調(diào)用疏旨,然后才執(zhí)行序列化操作很魂。__wakeup()
unserialize() 會(huì)檢查是否存在一個(gè) __wakeup() 方法。如果存在檐涝,則會(huì)先調(diào)用 __wakeup 方法遏匆,預(yù)先準(zhǔn)備對(duì)象需要的資源法挨。__toString()
__toString() 方法用于一個(gè)類被當(dāng)成字符串時(shí)應(yīng)怎樣回應(yīng)。例如 echo $obj; 應(yīng)該顯示些什么幅聘。
反序列化漏洞
PHP 中的魔術(shù)方法通常會(huì)因?yàn)槟承l件觸發(fā)執(zhí)行凡纳,所以在unserialize ()的參數(shù)可控時(shí),通過一定條件構(gòu)造惡意序列化代碼觸發(fā)魔術(shù)方法帝蒿,可造成嚴(yán)重代碼執(zhí)行等危害荐糜。
實(shí)例一
URL:http://120.79.33.253:9001/
對(duì)傳入的 str 參數(shù)值反序列化后與 KEY 的值相等即輸出flag
因此只需將 KEY 的值序列化一下然后傳給 str 參數(shù)即可
如圖編寫代碼獲取序列化值
傳值獲取 flag
實(shí)例二:__wakeup()魔術(shù)方法繞過(CVE-2016-7124)
- 漏洞影響版本:
PHP5 < 5.6.25
PHP7 < 7.0.10 - 漏洞產(chǎn)生原因:
如果存在__wakeup方法,調(diào)用 unserilize() 方法前則先調(diào)用__wakeup方法葛超,但是序列化字符串中表示對(duì)象屬性個(gè)數(shù)
的值大于
真實(shí)的屬性個(gè)數(shù)
時(shí)會(huì)跳過__wakeup的執(zhí)行 - 漏洞復(fù)現(xiàn)
編寫測試腳本
test.php
<?php
class test{
public $name = "fairy";
public function __wakeup(){
echo "this is __wakeup<br>";
}
public function __destruct(){
echo "this is __destruct<br>";
}
}
// $s = new test();
// echo serialize($s);
// $s = O:4:"test":1:{s:4:"name";s:5:"fairy";}
$str = $_GET["s"];
@$un_str = unserialize($str);
echo $un_str->name."<br>";
?>
腳本上標(biāo)明接收s參數(shù)暴氏,對(duì)其反序列化后輸出name屬性的值
為了方便觀察,我將傳入的s參數(shù)的name屬性值更改為xss代碼
訪問test.php
頁面顯示語句代表反序列化之前先調(diào)用了__wakeup 方法巩掺,
點(diǎn)擊確定后偏序,頁面完成后自動(dòng)執(zhí)行__destruct方法
將傳入的序列化數(shù)據(jù)的對(duì)象變量個(gè)數(shù)由1更改為2,頁面只執(zhí)行了__destruct方法胖替,而且輸出name屬性時(shí)報(bào)錯(cuò)研儒,是由于反序列化數(shù)據(jù)時(shí)失敗無法創(chuàng)建對(duì)象。
- 漏洞利用
更改測試代碼
test.php
<?php
class test{
public $name = "fairy";
public function __wakeup(){
echo "this is __wakeup<br>";
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
}
public function __destruct(){
echo "this is __destruct<br>";
$fp = fopen("D:\\phpStudy\\WWW\\wcute.php","w");
fputs($fp,$this->name);
fclose($fp);
}
}
// $s = new test();
// echo serialize($s);
// $s = O:4:"test":1:{s:4:"name";s:5:"fairy";}
$str = $_GET["s"];
@$un_str = unserialize($str);
echo $un_str->name."<br>";
?>
其中 __destruct方法在調(diào)用時(shí)將name參數(shù)寫入wcute.php文件
但是由于__wakeup方法清除了對(duì)象屬性独令,所以在調(diào)用__destruct時(shí)已經(jīng)沒有了name屬性端朵,因此文件將會(huì)寫入失敗,XSS代碼也不會(huì)執(zhí)行燃箭。
將對(duì)象屬性個(gè)數(shù)改為2繼續(xù)嘗試冲呢,成功繞過__wakeup方法執(zhí)行,將代碼寫入文件
參考鏈接:
https://secure.php.net/manual/zh/language.oop5.magic.php
https://blog.csdn.net/dyw_666666/article/details/90199606
https://xz.aliyun.com/t/378#toc-4