利用PHP 7中的OPcache來實(shí)現(xiàn)Webshell
在這篇文章中,我們將會(huì)對(duì)PHP7 OPcache引擎中的安全問題進(jìn)行講解,而且還會(huì)給大家介紹一種新型的漏洞利用技術(shù)。通過這種攻擊方法,我們可以繞過某些安全強(qiáng)化技術(shù),例如 禁止web目錄的文件讀寫 等安全保障措施。除此之外,攻擊者還可以利用這種攻擊技術(shù)在目標(biāo)主機(jī)中執(zhí)行惡意代碼。
OPcahceOPcache是PHP 7.0中內(nèi)嵌的新型緩存引擎。它可以對(duì)PHP腳本代碼進(jìn)行編譯,并且將編譯結(jié)果以字節(jié)碼的形勢(shì)存入內(nèi)存中。
OPcache 通過將 PHP 腳本預(yù)編譯的字節(jié)碼存儲(chǔ)到共享內(nèi)存中來提升 PHP 的性能, 存儲(chǔ)預(yù)編譯字節(jié)碼的好處就是省去了每次加載和解析 PHP 腳本的開銷。

除此之外,它還能提供文件系統(tǒng)的緩存功能,但是我們必須在PHP.ini配置文件中定義緩存信息的目標(biāo)文件夾路徑:
opcache.file_cache=/tmp/opcache
在上面這個(gè)文件夾中,OPcache會(huì)將編譯好的PHP腳本保存在相應(yīng)PHP腳本的相同目錄結(jié)構(gòu)之中。比如說,需要進(jìn)行編譯的代碼保存在/var/www/index.php之中,而完成編譯的代碼將會(huì)保存在/tmp/opcache/[system_id]/var/www/index.php.bin之中。
在上述文件路徑中,system_id是一個(gè)包含了當(dāng)前PHP版本信息,Zend框架的擴(kuò)展ID,以及各種數(shù)據(jù)類型信息的MD5 哈希值。在最新發(fā)行版的Ubuntu操作系統(tǒng)(16.04)之中,system_id是由當(dāng)前Zend框架和PHP的版本號(hào)所組成的(81d80d78c6ef96b89afaadc7ffc5d7ea)。當(dāng)OPcache首次對(duì)這些文件進(jìn)行緩存處理時(shí),會(huì)在文件系統(tǒng)中創(chuàng)建相應(yīng)的目錄。
正如我們將會(huì)在接下來的章節(jié)中看到的,每一個(gè)OPcache文件還會(huì)在文件的header域中保存system_id的副本。
至此,我們就不得不提到OPcache文件夾了,其中一個(gè)非常有趣的地方就是,當(dāng)用戶啟用了這項(xiàng)服務(wù)之后,用戶就會(huì)擁有OPcache生成的所有文件夾或者文件(所有文件和文件夾均位于/tmp/opcache/目錄之下)的寫入權(quán)限。
OPcache文件夾中的權(quán)限信息如下所示:
$ ls /tmp/opcache/
drwx------ 4 www-data www-data 4096 Apr 26 09:16 81d80d78c6ef96b89afaadc7ffc5d7ea
正如我們看到的那樣,www-data分組下的用戶都擁有OPcache所生成文件夾的寫入權(quán)限。如果我們擁有OPcache目錄的寫入權(quán)限,那么我們就可以重寫目錄中的緩存文件,然后利用webshell來執(zhí)行任意代碼。
攻擊場(chǎng)景首先,我們必須得到緩存文件夾的保存路徑(/tmp/opcache/[system_id]),以及目標(biāo)PHP文件的保存路徑(/var/www/...)。
為了讓大家更容易理解,我們假設(shè)網(wǎng)站目錄中存在一個(gè)phpinfo()文件,我們可以從這個(gè)文件中獲取到緩存文件夾和文件源代碼的存儲(chǔ)位置,當(dāng)我們?cè)谟?jì)算system_id的時(shí)候?qū)?huì)需要用到這些數(shù)據(jù)。我們已經(jīng)開發(fā)出了一款能夠從網(wǎng)站phpinfo()中提取信息,并計(jì)算system_id的工具。你可以在我們的 GitHub代碼庫(kù) 中獲取到這個(gè)工具。
在此,我們需要注意的是,目標(biāo)網(wǎng)站必須不能對(duì)上傳的文件有所限制。
現(xiàn)在,我們假設(shè)php.ini中除了默認(rèn)的設(shè)置信息之外,還添加有下列配置數(shù)據(jù):
opcache.validate_timestamp = 0 ; PHP 7’s default is 1
opcache.file_cache_only = 1 ; PHP 7’s default is 0
opcache.file_cache = /tmp/opcache
接下來,我們就會(huì)給大家講解攻擊的實(shí)施過程:
我們已經(jīng)在網(wǎng)站中找到了一個(gè)漏洞,這個(gè)漏洞意味著網(wǎng)站不會(huì)對(duì)我們上傳的文件進(jìn)行任何的限制。我們的目標(biāo)就是利用包含后門的惡意代碼替換掉文件/tmp/opcache/[system_id]/var/www/index.php.bin。

上圖顯示的就是包含漏洞的網(wǎng)站界面。
1.在本地創(chuàng)建一個(gè)包含Webshell的惡意PHP文件,將其命名為“index.php”:
<?php
system($_GET[’cmd’]);
?>
2.將opcache.file_cache的相關(guān)設(shè)置添加進(jìn)你的PHP.ini文件之中。
3.利用php –S 127.0.0.1:8080命令啟動(dòng)一個(gè)Web服務(wù)器,然后向服務(wù)器請(qǐng)求index.php文件,并觸發(fā)緩存引擎。在這一步中,我們只需要使用命令wget 127.0.0.1:8080就可以實(shí)現(xiàn)我們的目的了。
4.定位到我們?cè)诘谝徊街性O(shè)置的緩存文件夾,你將會(huì)發(fā)現(xiàn)了一個(gè)名為index.php.bin的文件。這個(gè)文件就是已經(jīng)經(jīng)過編譯處理的webshell。

上圖顯示的是OPcache生成的index.php.bin。
5.由于本地system_id很可能與目標(biāo)主機(jī)的system_id不同,所以我們必須打開index.php.bin文件,并將我們的system_id修改成目標(biāo)主機(jī)的system_id。正如我們之前所提到的,system_id是有可能被猜到的,例如暴力破解,或者根據(jù)phpinfo()文件中的服務(wù)器信息計(jì)算出來。我們可以在文件簽名數(shù)據(jù)之后修改system_id,具體如下圖所示:

上圖顯示的是system_id的數(shù)據(jù)存儲(chǔ)位置。
6.由于目標(biāo)網(wǎng)站對(duì)上傳的文件沒有任何的限制,所以我們現(xiàn)在就將文件上傳至服務(wù)器的目錄之中:
/tmp/opcache/[system_id]/var/www/index.php.bin
7.刷新網(wǎng)站的index.php,網(wǎng)站將會(huì)自動(dòng)執(zhí)行我們的webshell。

繞過內(nèi)存緩存(file_cache_only = 0)
如果內(nèi)存緩存的優(yōu)先級(jí)高于文件緩存,那么重寫OPcache文件并不會(huì)執(zhí)行我們的webshell。如果服務(wù)器托管的網(wǎng)站中存在文件上傳限制漏洞,那么在服務(wù)器重啟之后,我們就可以繞過這種限制。既然內(nèi)存緩存可以被清空,OPcache將會(huì)使用文件緩存來填充內(nèi)存緩存,從而達(dá)到我們執(zhí)行webshell的目的。
這也就意味著,我們還是有辦法能夠在服務(wù)器不進(jìn)行重啟的情況下,執(zhí)行我們的webshell。
在WordPress等網(wǎng)站框架之中,還是會(huì)有一些過時(shí)的文件可以公開訪問到,例如 registration-functions.php 。
由于這些文件已經(jīng)過時(shí)了,所以系統(tǒng)不會(huì)再加載這些文件,這也就意味著,內(nèi)存和文件系統(tǒng)的緩存中是不可能存在有這些文件的。當(dāng)我們上傳了惡意代碼(registration-functions.php.bin)之后,然后訪問相關(guān)的網(wǎng)頁(yè)(/wp-includes/registration-functions.php),OPcache就會(huì)自動(dòng)執(zhí)行我們的webshell。
繞過時(shí)間戳認(rèn)證(validate_timestamps = 1)
時(shí)間戳通常是一個(gè)字符序列,它可以唯一地標(biāo)識(shí)某一時(shí)刻的時(shí)間。一般來說,時(shí)間戳產(chǎn)生的過程為:用戶首先將需要加時(shí)間戳的文件用Hash編碼加密形成摘要,然后將該摘要發(fā)送到DTS,DTS在加入了收到文件摘要的日期和時(shí)間信息后再對(duì)該文件加密(數(shù)字簽名),然后送回用戶。
如果服務(wù)器啟用了時(shí)間戳認(rèn)證功能,OPcache將會(huì)對(duì)被請(qǐng)求的PHP源文件的時(shí)間戳進(jìn)行驗(yàn)證,如果該文件與緩存文件header域中的時(shí)間戳相匹配,那么服務(wù)器就會(huì)允許訪問。如果時(shí)間戳不匹配,那么緩存文件將會(huì)被丟棄,并創(chuàng)建出一個(gè)新的緩存文件。為了繞過這種限制,攻擊者必須知道目標(biāo)源文件的時(shí)間戳。
這也就意味著,在WordPress等網(wǎng)站框架之中,源文件的時(shí)間戳是可以獲取到的,因?yàn)楫?dāng)開發(fā)人員將代碼文件從壓縮包中解壓出來之后,時(shí)間戳信息仍然是保持不變的。

上圖顯示的是WordPress/wp-includes文件夾中的信息。
有趣的是,其中的有些文件從2012年起就再也沒有進(jìn)行過任何的修改(請(qǐng)注意以下兩個(gè)文件:registration-functions.php和registration.php)。因此,即使是不同版本的WordPress,這些相同文件的時(shí)間戳也是一樣的。在獲取到了文件時(shí)間戳的信息之后,攻擊者就可以修改他們的惡意代碼,并且成功覆蓋服務(wù)器的緩存數(shù)據(jù)。時(shí)間戳信息位于文件開頭處的第34字節(jié)位置:

在此,我們給大家提供了一個(gè)簡(jiǎn)短的演示視頻,并在視頻中對(duì)攻擊步驟進(jìn)行了講解:
視頻地址:https://youtu.be/x42l-PQHhbA
正如我們?cè)诖酥疤岬降?大家可以在我們的 GitHub代碼庫(kù) 中獲取到你們所需要的工具。
總結(jié)總而言之,這種新型的攻擊方法并不會(huì)對(duì)使用PHP進(jìn)行開發(fā)的應(yīng)用程序產(chǎn)生影響,因?yàn)檫@并不是PHP的通用漏洞。現(xiàn)在,很多Linux發(fā)行版的操作系統(tǒng)(例如Ubuntu 16.04)都會(huì)默認(rèn)安裝PHP 7,所以當(dāng)我們?cè)诹私獾搅诉@種攻擊技術(shù)之后,在我們的開發(fā)過程中,更加因該謹(jǐn)慎地審查我們的代碼,并檢查網(wǎng)站中是否存在文件上傳限制漏洞,因?yàn)檫@個(gè)漏洞將會(huì)對(duì)服務(wù)器的安全產(chǎn)生影響。
本文由 360安全播報(bào) 翻譯,轉(zhuǎn)載請(qǐng)注明“轉(zhuǎn)自360安全播報(bào)”,并附上鏈接。
原文鏈接:http://blog.gosecure.ca/2016/04/27/binary-webshell-through-opcache-in-php-7/
相關(guān)文章:
1. 使用Python webdriver圖書館搶座自動(dòng)預(yù)約的正確方法2. Android 簡(jiǎn)單的實(shí)現(xiàn)滑塊拼圖驗(yàn)證碼功能3. PHP如何開啟Opcache功能提升程序處理效率4. Python3 json模塊之編碼解碼方法講解5. Python 合并拼接字符串的方法6. 在線php代碼縮進(jìn)、代碼美化工具:PHP Formatter7. 淺談?dòng)蓀osition屬性引申的css進(jìn)階討論8. Linux刪除系統(tǒng)自帶版本Python過程詳解9. Android Studio實(shí)現(xiàn)格式化XML代碼順序10. ASP.NET MVC使用jQuery ui的progressbar實(shí)現(xiàn)進(jìn)度條

網(wǎng)公網(wǎng)安備