0x00 序列化和反序列化
簡單的理解:序列化就是使用serialize()將對象的用字符串的方式進(jìn)行表示,反序列化是使用unserialize()將序列化的字符串膏燕,構(gòu)造成相應(yīng)的對象芜飘,反序列化是序列化的逆過程腻格。 序列化的對象可以是class也可以是Array,string等其他對象淹朋。
0x01 對象序列化和反序列化的功能作用
1. 對象序列化的功能作用
概念:對象是在內(nèi)存中存儲(chǔ)的數(shù)據(jù)類型绞灼,壽命通常隨著生成該對象的程序的終止而終止施逾,但是有些情況下需要將對象的狀態(tài)保存下來敷矫,然后在需要使用的時(shí)候?qū)ο蠡謴?fù),對象狀態(tài)的保存操作就是對象序列化的過程汉额。對象序列化就是將對象轉(zhuǎn)化為2進(jìn)制字符串進(jìn)行保存曹仗。?
作用:將對象的狀態(tài)通過數(shù)值和字符記錄下來,以某種存儲(chǔ)形式使自定義對象持久化蠕搜,方便需要時(shí)候?qū)ο筮M(jìn)行恢復(fù)使用怎茫,用于對象的傳遞以及使程序代碼更具維護(hù)性
?語法:在創(chuàng)建對象class后使用serialize()函數(shù)將聲明的對象的某個(gè)狀態(tài)轉(zhuǎn)化為字符串然后進(jìn)行保存或傳遞。
示例代碼: class serialize code:
output:?
O:6:"Person":2:{s:12:" Person name";s:8:"Thinking";s:11:" Person sex";s:3:"man";} save data is: O:6:"Person":2:{s:12:" Person name";s:8:"Thinking";s:11:" Person sex";s:3:"man";}__
array serialize code:
output:?
a:2:{s:4:"name";s:8:"Thinking";s:3:"sex";s:3:"man";} save data is: a:2:{s:4:"name";s:8:"Thinking";s:3:"sex";s:3:"man";}
序列化后對象的格式: 引用上述示例代碼中的輸出結(jié)果 妓灌。
output:
O:6:"Person":2:{s:12:" Person name";s:8:"Thinking";s:11:" Person sex";s:3:"man";} a:2:{s:4:"name";s:8:"Thinking";s:3:"sex";s:3:"man";}
對象類型:對象名長度:“對象名”:對象成員變量個(gè)數(shù):{變量1類型:變量名1長度:變量名1; 參數(shù)1類型:參數(shù)1長度:參數(shù)1; 變量2類型:變量名2長度:“變量名2”; 參數(shù)2類型:參數(shù)2長度:參數(shù)2;… …}
對象類型:Class:用O表示轨蛤,Array:用a表示。?
變量和參數(shù)類型:string:用s表示,Int:用i表示,Array:用a表示虫埂。?
序列符號:參數(shù)與變量之間用分號(;)隔開,同一變量和同一參數(shù)之間的數(shù)據(jù)用冒號(:)隔開祥山。
2. 對象反序列化的功能作用
概念:將存儲(chǔ)好的或者進(jìn)行傳遞的序列化后的字符串轉(zhuǎn)化為對象,然后在用于對象的操作掉伏,是序列化的逆過程 缝呕。
作用:把序列化后的字符串轉(zhuǎn)化為對象,恢復(fù)原本對象后用于程序或代碼的各種操作斧散。
語法:使用unserialize()將序列化后的字符串轉(zhuǎn)化為對象進(jìn)行使用供常。
示例代碼:
unserialize code:
output:
0x02 反序列化存在的問題
問題原因:漏洞的根源在于unserialize()函數(shù)的參數(shù)可控。如果反序列化對象中存在魔術(shù)方法鸡捐,而且魔術(shù)方法中的代碼或變量用戶可控栈暇,就可能產(chǎn)生反序列化漏洞,根據(jù)反序列化后不同的代碼可以導(dǎo)致各種攻擊箍镜,如代碼注入源祈、SQL注入、目錄遍歷等等鹿寨。
魔術(shù)方法:PHP的類中可能會(huì)包含一些特殊的函數(shù)叫魔術(shù)函數(shù)新博,魔術(shù)函數(shù)命名是以符號__開頭的;?
有以下的魔術(shù)方法: __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set(), _state(), __clone(), __debugInfo() ...
反序列化漏洞中常見到有一些魔術(shù)方法:
?__construct():在對象創(chuàng)建時(shí)自動(dòng)被調(diào)用; __destruct():在腳本運(yùn)行結(jié)束時(shí)自動(dòng)被調(diào)用脚草;__sleep():在對象序列化的時(shí)候自動(dòng)被調(diào)用赫悄;__wakeup():在反序列化為對象時(shí)自動(dòng)被調(diào)用;__toString(): 直接輸出對象引用時(shí)自動(dòng)被調(diào)用;
0x03 魔術(shù)方法的觸發(fā)
構(gòu)造方法 __construct()?
構(gòu)造方法是類中的一個(gè)特殊方法埂淮。當(dāng)使用 new 操作符創(chuàng)建一個(gè)類的實(shí)例時(shí)姑隅,構(gòu)造方法將會(huì)自動(dòng)調(diào)用,其名稱必須是 __construct()倔撞。在一個(gè)類中只能聲明一個(gè)構(gòu)造方法讲仰,而是只有在每次創(chuàng)建對象的時(shí)候都會(huì)去調(diào)用一次構(gòu)造方法,不能主動(dòng)的調(diào)用這個(gè)方法痪蝇,所以通常用它執(zhí)行一些有用的初始化任務(wù)鄙陡。該方法無返回值。?
語法: function __construct(arg1,arg2,…) { …… }
[example]:__construct() code:
output: __construct is work
析構(gòu)方法__destruct()允許在銷毀一個(gè)類之前執(zhí)行執(zhí)行析構(gòu)方法躏啰,與構(gòu)造方法對應(yīng)的就是析構(gòu)方法趁矾,析構(gòu)方法允許在銷毀一個(gè)類之前執(zhí)行的一些操作或完成一些功能,比如說關(guān)閉文件给僵、釋放結(jié)果集毫捣,程序運(yùn)行結(jié)束等。析構(gòu)函數(shù)不能帶有任何參數(shù)帝际,其名稱必須是 __destruct()蔓同。
?語法: function __destruct() { …… }
[example]:__destruct code:
output: 先延遲5s,等待代碼執(zhí)行結(jié)束 蹲诀,再打印 __destruct ?is work斑粱。
__sleep()方法是在一個(gè)類的實(shí)例被序列化了的時(shí)候調(diào)用,_wakeup()是在反序列化時(shí)被調(diào)用侧甫。__sleep()必須返回一個(gè)數(shù)組或者對象珊佣,而一般返回的是當(dāng)前對象$this蹋宦。返回的值將會(huì)被用來做序列化的值披粟。如果不返回這個(gè)值,自然表示序列化失敗冷冗。同時(shí)也會(huì)連累到反序列化時(shí)不會(huì)調(diào)用__wakeup()方法守屉。
[example]:__sleep()``__wakeup()code:
output: __sleep is work __wakeup is work
如果我們想打印出一個(gè)對象,就需要調(diào)用__toString()這個(gè)魔術(shù)方法了,該方法會(huì)在直接輸出對象引用時(shí)自動(dòng)被調(diào)用蒿辙,此方法必須返回一個(gè)字符串拇泛,否則將發(fā)出一條 E_RECOVERABLE_ERROR 級別的致命錯(cuò)誤 參考:http://php.net/__toString
[example]:__toString()code:
output: __toString is work
其他方法的介紹參考:
?http://www.5idev.com/p-php_member_overloading.shtml ?http://php.net/__toString
0x04 一道CTF中反序列化例題
2016xctf的反序列化題目
index.php的源碼:
class.php的源碼:
源碼分析:?
首先index.php源碼中的第6行使用file_get_contents讀取user參數(shù)的值,然后在源碼的第6思灌,11行存在文件包含俺叭,第12行 unserialize($pass)反序列化函數(shù)的參數(shù)可控,在第13行執(zhí)行了 echo $pass; 在class.php源碼中使用了__toString()魔術(shù)方法泰偿,然后return "__toString was called!";,所以根據(jù)本篇的上半部分介紹此處滿足__toString()魔術(shù)方法觸發(fā)條件熄守,所以存在反序列化漏洞,其中第6行file_get_contents是用來讀取$file變量的文件的,并且給出了提示裕照,//f1a9.php攒发; 所以本題的考點(diǎn)就是利用文件包含使用php://input的封裝協(xié)議傳入user參數(shù)的值,滿足index.php源碼中的第6行的條件晋南,在pass參數(shù)中傳入序列化后要讀取的flag文件惠猿。
最終PAYLOAD:
首先構(gòu)造序列化的字符串O:4:"Read":1:{s:4:"file";s:57:"php://filter/read=convert.base64-encode/resource=f1a9.php";}然后進(jìn)行如下請求。?
GET DATA :?user=php://input&file=class.php&pass=O:4:”Read”:1:{s:4:”file”;s:57:”php://filter/read=convert.base64-encode/resource=f1a9.php”;}?
POST DATA:the user is admin
0x05 小總結(jié)
本篇僅進(jìn)行了部分魔術(shù)方法的總結(jié)负间,還有一些魔術(shù)方法后續(xù)將逐步補(bǔ)充偶妖,例題僅收集了1道,小伙伴們有其他例題也可提出政溃,小編將在后續(xù)篇章繼續(xù)總結(jié)餐屎。從ctf題目中體會(huì)反序列化漏洞的形成原因和利用方法是個(gè)不錯(cuò)的方式,期待大家的多多交流玩祟。