您好,登錄后才能下訂單哦!
小編給大家分享一下PHP中對象注入的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
前言
雖然這篇文章叫做PHP對象注入,但是本質上還是和PHP的序列化的不正確使用有關。如果你閱讀了PHP中的SESSION反序列化機制對序列化就會有一個大致的認識。PHP對象注入其實本質上也是由于序列化引起的。
基礎知識
在php類中可能會存在一些叫做魔術函數(magic 函數),這些函數會在類進行某些事件的時候自動觸發,例如__construct()
會在一個對象被創建時調用, __destruct()
會在一個對象銷毀時調用, __toString
當對象被當做一個字符串的時候被調用。常見的魔術函數有__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(); //實例化對象,調用__construct()方法,輸出__construct $obj->echoP(); //調用echoP()方法,輸出"abc" echo $obj; //obj對象被當做字符串輸出,調用__toString()方法,輸出__toString $s =serialize($obj); //obj對象被序列化,調用__sleep()方法,輸出__sleep echo unserialize($s); //$s首先會被反序列化,會調用__wake()方法,被反序列化出來的對象又被當做字符串,就會調用_toString()方法。 // 腳本結束又會調用__destruct()方法,輸出__destruct ?>
原理
為什么會用到序列話這樣的方法?主要就是就是方便進行數據的傳輸,并且數據恢復之后,數據的屬性還不會發生變化。例如,將一個對象反序列化之后,還是保存了這個對象的所有的信息。同時還可以將序列化的值保存在文件中,這樣需要用的時候就可以直接從文件中讀取數據然后進行反序列化就可以了。在PHP使用serialize()
和unserialize()
來進行序列化和反序列化的。
而序列化的危害就在于如果序列化的內容是用戶可控的,那么用戶就可以注入精心構造的payload。當進行發序列化的時候就有可能會出發對象中的一些魔術方法,造成意想不到的危害。
對象注入
本質上serialize()
和unserialize()
在PHP內部實現上是沒有漏洞的,漏洞的主要產生是由于應用程序在處理對象、魔術函數以及序列化相關問題的時候導致的。
如果在一個程序中,一個類用于臨時將日志存儲進某個文件中,當__destruct()
方法被調用時,日志文件被刪除。
代碼大致如下:
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('記錄日志'); ?>
上面的這段代碼就是一個正常的使用LogClass類來完成日志記錄的功能。
下面顯示的是存在對象注入漏洞的使用例子。
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/>"; } } // 從用戶接受輸入發序列化為User對象 $usr = unserialize($_GET["user"]); ?>
上面顯示的代碼使用了LogClass對象同時還會從用戶那里接受輸入進行發序列化轉化為一個User對象。
當我們提交如下的數據
news.php?user=O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John”;}
這樣的語句是可以正常使用的,也是程序員希望使用的方法。
但是如果提交的數據為:
news.php?user=O:8:"LogClass":1:{s:11:"logfilename";s:9:".htaccess";}
那么最后就會輸出delete .htaccess
。
可以看到通過構造的數據,導致執行了LogClass中的__destruct()
方法然后刪除了網站中重要的配置文件。
從上面這個例子也可以看出來,如果沒有嚴格控制用戶的輸入同時對用戶的輸入進行了反序列化的操作,那么就有可能會實現代碼執行的漏洞。
注入點
PHP對象注入一般在處在程序的邏輯上面。例如一個User類定義了__toString()
用來進行格式化輸出,但是也存在File類定義了__toString()
方法讀取文件內容然后進行顯示,那么攻擊者就有可能通過User類的反序列化構造一個File類來讀取網站的配置文件。
user.php
<?php class FileClass { public $filename = "error.log"; public function __toString() { echo "filename發生了變化==>" . $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; //調用obj的__toString()方法 ?>
正常情況下我們應該傳入UserClass序列化的字符串,例如user.php?usr=O:9:"UserClass":2:{s:3:"age";i:18;s:4:"name";s:3:"Tom";}
,頁面最后就會輸出User Tom is 18 years old.
。
這也是一個理想的使用方法。
但是如果我們傳入的數據為user.php?usr=O:9:"FileClass":1:{s:8:"filename";s:10:"config.php";}
,頁面最后的輸出是filename發生了變化==>config.php,執行了FileClass中的__toString()
方法。
這樣就可以讀取到config.php中的源代碼了。
漏洞挖掘
這類洞一般都是很難挖掘的,雖然顯示看起來很簡單,但實際上需要的條件還是相當的苛刻的,而且找對象注入的漏洞一般都是通過審計源代碼的方式來進行尋找,看unserialize()
的參數是否是可控的,是否存在反序列化其他參數對象的可能。
防御
要對程序中的各種邊界條件進行測試
避免用戶對于unserialize()
參數是可控的,可以考慮使用json_decode方法來進行傳參。
以上是“PHP中對象注入的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。