深入講解PHP的對(duì)象注入(Object Injection)
前言
雖然這篇文章叫做PHP對(duì)象注入,但是本質(zhì)上還是和PHP的序列化的不正確使用有關(guān)。如果你閱讀了PHP中的SESSION反序列化機(jī)制對(duì)序列化就會(huì)有一個(gè)大致的認(rèn)識(shí)。PHP對(duì)象注入其實(shí)本質(zhì)上也是由于序列化引起的。
基礎(chǔ)知識(shí)
在php類中可能會(huì)存在一些叫做魔術(shù)函數(shù)(magic 函數(shù)),這些函數(shù)會(huì)在類進(jìn)行某些事件的時(shí)候自動(dòng)觸發(fā),例如__construct()
會(huì)在一個(gè)對(duì)象被創(chuàng)建時(shí)調(diào)用, __destruct()
會(huì)在一個(gè)對(duì)象銷毀時(shí)調(diào)用, __toString
當(dāng)對(duì)象被當(dāng)做一個(gè)字符串的時(shí)候被調(diào)用。常見(jiàn)的魔術(shù)函數(shù)有__construct()
、 __destruct()
、 __toString()
、 __sleep()
、 __wakeup()
。
舉例如下:
<?php class test{ public $varr1="abc"; public $varr2="123"; public function echoP(){ echo $this->varr1."<br>"; } public function __construct(){ echo "__construct<br>"; } public function __destruct(){ echo "__destruct<br>"; } public function __toString(){ return "__toString<br>"; } public function __sleep(){ echo "__sleep<br>"; return array('varr1','varr2'); } public function __wakeup(){ echo "__wakeup<br>"; } } $obj = new test(); //實(shí)例化對(duì)象,調(diào)用__construct()方法,輸出__construct $obj->echoP(); //調(diào)用echoP()方法,輸出"abc" echo $obj; //obj對(duì)象被當(dāng)做字符串輸出,調(diào)用__toString()方法,輸出__toString $s =serialize($obj); //obj對(duì)象被序列化,調(diào)用__sleep()方法,輸出__sleep echo unserialize($s); //$s首先會(huì)被反序列化,會(huì)調(diào)用__wake()方法,被反序列化出來(lái)的對(duì)象又被當(dāng)做字符串,就會(huì)調(diào)用_toString()方法。 // 腳本結(jié)束又會(huì)調(diào)用__destruct()方法,輸出__destruct ?>
原理
為什么會(huì)用到序列話這樣的方法?主要就是就是方便進(jìn)行數(shù)據(jù)的傳輸,并且數(shù)據(jù)恢復(fù)之后,數(shù)據(jù)的屬性還不會(huì)發(fā)生變化。例如,將一個(gè)對(duì)象反序列化之后,還是保存了這個(gè)對(duì)象的所有的信息。同時(shí)還可以將序列化的值保存在文件中,這樣需要用的時(shí)候就可以直接從文件中讀取數(shù)據(jù)然后進(jìn)行反序列化就可以了。在PHP使用serialize()
和unserialize()
來(lái)進(jìn)行序列化和反序列化的。
而序列化的危害就在于如果序列化的內(nèi)容是用戶可控的,那么用戶就可以注入精心構(gòu)造的payload。當(dāng)進(jìn)行發(fā)序列化的時(shí)候就有可能會(huì)出發(fā)對(duì)象中的一些魔術(shù)方法,造成意想不到的危害。
對(duì)象注入
本質(zhì)上serialize()
和unserialize()
在PHP內(nèi)部實(shí)現(xiàn)上是沒(méi)有漏洞的,漏洞的主要產(chǎn)生是由于應(yīng)用程序在處理對(duì)象、魔術(shù)函數(shù)以及序列化相關(guān)問(wèn)題的時(shí)候?qū)е碌摹?br />
如果在一個(gè)程序中,一個(gè)類用于臨時(shí)將日志存儲(chǔ)進(jìn)某個(gè)文件中,當(dāng)__destruct()
方法被調(diào)用時(shí),日志文件被刪除。
代碼大致如下:
logfile.php
<?php class LogClass { public $logfilename = ""; public function logdata($text) { echo "log data".$text."<br/>"; file_put_contents($this->logfilename,$text,FILE_APPEBD); } public function __destruct() { echo 'deletes'.$this->logfilename; unlink(dirname(__FILE__).'/'.$this->logfilename); } } ?>
在其他類中使用LogClass
logLogin.php
<?php include "index.php"; $obj = new LogClass(); $obj->logfilename = "login.log"; $obj->logdata('記錄日志'); ?>
上面的這段代碼就是一個(gè)正常的使用LogClass類來(lái)完成日志記錄的功能。
下面顯示的是存在對(duì)象注入漏洞的使用例子。
news.php
<?php include "logfile.php"; // some codes the use the LogClass class User { public $age = 0; public $name = ''; public function print_data() { echo "User".$this->name."is".$this->age."years old.<br/>"; } } // 從用戶接受輸入發(fā)序列化為User對(duì)象 $usr = unserialize($_GET["user"]); ?>
上面顯示的代碼使用了LogClass對(duì)象同時(shí)還會(huì)從用戶那里接受輸入進(jìn)行發(fā)序列化轉(zhuǎn)化為一個(gè)User對(duì)象。
當(dāng)我們提交如下的數(shù)據(jù)
news.php?user=O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John”;}
這樣的語(yǔ)句是可以正常使用的,也是程序員希望使用的方法。
但是如果提交的數(shù)據(jù)為:
news.php?user=O:8:"LogClass":1:{s:11:"logfilename";s:9:".htaccess";}
那么最后就會(huì)輸出delete .htaccess
。
可以看到通過(guò)構(gòu)造的數(shù)據(jù),導(dǎo)致執(zhí)行了LogClass中的__destruct()
方法然后刪除了網(wǎng)站中重要的配置文件。
從上面這個(gè)例子也可以看出來(lái),如果沒(méi)有嚴(yán)格控制用戶的輸入同時(shí)對(duì)用戶的輸入進(jìn)行了反序列化的操作,那么就有可能會(huì)實(shí)現(xiàn)代碼執(zhí)行的漏洞。
注入點(diǎn)
PHP對(duì)象注入一般在處在程序的邏輯上面。例如一個(gè)User類定義了__toString()
用來(lái)進(jìn)行格式化輸出,但是也存在File類定義了__toString()
方法讀取文件內(nèi)容然后進(jìn)行顯示,那么攻擊者就有可能通過(guò)User類的反序列化構(gòu)造一個(gè)File類來(lái)讀取網(wǎng)站的配置文件。
user.php
<?php class FileClass { public $filename = "error.log"; public function __toString() { echo "filename發(fā)生了變化==>" . $this->filename ; return @file_get_contents($this->filename); } } class UserClass { public $age = 0; public $name = ''; public function __toString() { return 'User '.$this->name." is ".$this->age.' years old. <br/>'; } } $obj = unserialize($_GET['usr']); echo $obj; //調(diào)用obj的__toString()方法 ?>
正常情況下我們應(yīng)該傳入U(xiǎn)serClass序列化的字符串,例如user.php?usr=O:9:"UserClass":2:{s:3:"age";i:18;s:4:"name";s:3:"Tom";}
,頁(yè)面最后就會(huì)輸出User Tom is 18 years old.
。
這也是一個(gè)理想的使用方法。
但是如果我們傳入的數(shù)據(jù)為user.php?usr=O:9:"FileClass":1:{s:8:"filename";s:10:"config.php";}
,頁(yè)面最后的輸出是filename發(fā)生了變化==>config.php,執(zhí)行了FileClass中的__toString()
方法。
這樣就可以讀取到config.php中的源代碼了。
漏洞挖掘
這類洞一般都是很難挖掘的,雖然顯示看起來(lái)很簡(jiǎn)單,但實(shí)際上需要的條件還是相當(dāng)?shù)目量痰?,而且找?duì)象注入的漏洞一般都是通過(guò)審計(jì)源代碼的方式來(lái)進(jìn)行尋找,看unserialize()
的參數(shù)是否是可控的,是否存在反序列化其他參數(shù)對(duì)象的可能。
防御
要對(duì)程序中的各種邊界條件進(jìn)行測(cè)試
避免用戶對(duì)于unserialize()
參數(shù)是可控的,可以考慮使用json_decode方法來(lái)進(jìn)行傳參。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
對(duì)PHP PDO的一些認(rèn)識(shí)小結(jié)
這篇文章主要介紹了對(duì)PHP PDO的一些認(rèn)識(shí)小結(jié),本文講解了什么是PDO、啟用PDO的配置方法、PDO的預(yù)定義類、事務(wù)處理例子等內(nèi)容,需要的朋友可以參考下2015-01-01解析使用substr截取UTF-8中文字符串出現(xiàn)亂碼的問(wèn)題
本篇文章是對(duì)使用substr截取UTF-8中文字符串出現(xiàn)亂碼的問(wèn)題進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06PHP生成不同顏色、不同大小的tag標(biāo)簽函數(shù)
看別人網(wǎng)站上面的tag都是不同顏色,不同大小的tag標(biāo)簽非常不錯(cuò),這里就分享兩個(gè)函數(shù),方便需要的朋友2013-09-09PHP圖像處理 imagestring添加圖片水印與文字水印操作示例
這篇文章主要介紹了PHP圖像處理 imagestring添加圖片水印與文字水印操作,結(jié)合實(shí)例形式分析了PHP使用imagestring添加圖片水印與文字水印具體操作步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2020-02-02php過(guò)濾所有的空白字符(空格、全角空格、換行等)
這篇文章主要介紹了php替換過(guò)濾所有的空白字符,包括空格、全角空格、換行等,感興趣的小伙伴們可以一起學(xué)習(xí)學(xué)習(xí)。2015-10-10