如何繞過PHP中被禁用的函式
一、前言
想象一下,假如某天我們發現了一個未受限制的檔案上傳漏洞,已經將web shell上傳到目標伺服器上;或者假如我們可以通過 ofollow,noindex" target="_blank">LFI (Local File Inclusion,本地檔案包含)或 RFI (Remote File Inclusion,遠端檔案包含)漏洞在目標系統上執行命令,正準備大幹一場。
當我們執行某些命令後,希望能夠在服務端呼叫系統函式時,卻看到突如其來的一個警告,聲稱由於該函式已被禁用,因此不能呼叫:
www.example.com/shell.php?cmd=whoami Warning: system() has been disabled for security reasons in /var/www/html/shell.php on line 6
我們可以在 php.ini
配置檔案中配置 disable_functions
選項,禁用某些函式。為了加固系統安全,通常的建議是通過 disable_functions
選項禁用 system
、 exec
、 shell_exec
以及 passthru
等函式。然而,最近Twoster在俄羅斯 Antichat 論壇上公佈了一個新的方法,能夠繞過這種安全機制。在本文中我們會與大家分享這方面技術細節。
二、繞過方法
在Antichat論壇上公佈後該方法後,Anton Lopanitsyn上週也在 PHP_imap_open_exploit/blob/master/exploit.php" rel="nofollow,noindex" target="_blank">Github 上分享了利用程式碼。 在利用程式碼中,我們可以發現這種繞過方法依賴的是 imap_open() 函式,在PHP上安裝 imap
擴充套件後就會啟用該函式。
<?php # CRLF (c) # echo '1234567890'>/tmp/test0001 $server = "x -oProxyCommand=echotZWNobyAnMTIzNDU2Nzg5MCc+L3RtcC90ZXN0MDAwMQo=|base64t-d|sh}"; imap_open('{'.$server.':143/imap}INBOX', '', '') or die("nnError: ".imap_last_error());
PHP函式庫(core)中並不包含 imap_open()
函式,該函式是 imapd
的一個封裝函式,由華盛頓大學的研究人員開發。前文提到過,只有當我們安裝 IMAP
PHP擴充套件後,PHP才會包含 imap_open()
函式。接下來讓我們逐步分析利用程式碼中的每個元件。
imap_open
函式引數
我們先來觀察一下 mailbox
引數,理解利用程式碼中 imap_open
函式的作用。該函式的語法如下:
resource imap_open ( string $mailbox , string $username , string $password [, int $options = 0 [, int $n_retries = 0 [, array $params = NULL ]]] )
mailbox
引數的值由伺服器名和伺服器上的mailbox檔案路徑所組成, INBOX
代表的是當前使用者的個人郵箱。比如,我們可以通過如下方式來設定 mailbox
引數:
$mbox = imap_open ("{localhost:993/PROTOCOL/FLAG}INBOX", "user_id", "password");
在括號內的字串中,我們可以看到伺服器名稱(或者IP地址)、埠號以及協議名稱。使用者可以在協議名後設置標誌(第3個引數)。
在PHP官方文件中,關於 imap_open
引數的設定有如下一段警告內容:
根據警告資訊,除非我們禁用了 enable_insecure_rsh
選項,否則不要將使用者資料直接傳輸到 mailbox
引數中。現在讓我們看看 IMAP
擴充套件的工作過程,理解 enable_insecure_rsh
選項所發揮的作用,以及官方文件為什麼建議使用者禁用該選項。
IMAP伺服器型別及SSH連線
現在有兩種基於Unix的IMAP伺服器被人們廣泛使用,一種為華盛頓大學開發的 imapd
,另一種為Cyrus開發的IMAP伺服器。
Cyrus會將使用者郵件儲存到內建資料庫中,只有通過IMAP協議才能訪問Cyrus。因此,當使用Cyrus時,已安裝IMAP的Unix系統上的使用者賬戶與IMAP賬戶之間並沒有任何關聯。
另一方面, imapd
會將郵箱儲存到檔案中,而這些檔案由Unix系統中郵件使用者所有,如 /var/spool/mail
,因此 imapd
對應的使用者賬戶以及訪問許可權與Unix伺服器直接相關。如果郵件存放在我們具備訪問許可權的 spool
檔案中,我們可以通過SSH方式登入系統,驗證我們對這些檔案的訪問許可權。
當能夠使用SSH時,整個過程並不需要建立 IMAP
連線。 imap_open
函式首先會建立SSH連線,如果認證通過,則會在沒有 IMAP
連線的情況下繼續執行,這就是所謂的 IMAP
預認證模式。基於這一點,我們才會看到前面提到的關於 mailbox
引數的警告資訊。在設定 SSH
連線時, mailbox
引數的值會以引數形式傳遞給SSH命令。
在安全SSH協議被廣泛使用之前,還有一個名為 rsh
的協議。然而預設情況下這種協議非常不安全,沒有使用加密技術,因此不應當在本地網路環境外使用(甚至也不要在本地網路環境中使用)。 imap.enable_insecure_rsh
配置選項可以用來啟用預認證模式中的 rsh
及 ssh
協議。
-oProxyCommand
引數
SSH
命令中用到了許多命令,其中我們可以使用 -o
引數來設定連線期間可用的各種選項。在建立SSH連線之前,我們可以設定 ProxyCommand
引數,如下所示:
ssh -oProxyCommand="touch tmp.txt" localhost
當我們執行這條命令時,可以發現即便我們沒有建立與 localhost
的SSH連線,也會建立 tmp.txt
檔案。
根據前面的分析,即使當前系統禁用了 system
、 passthru
等命令,如果存在RFI或者LFI漏洞,目標系統還是有命令執行風險。
三、緩解措施
我們可以採取兩種措施來緩解 imap
PHP擴充套件所帶來的風險。第一種方法是檢查傳遞給 imap_open
的使用者輸入引數中是否存在任何特殊字元(如斜槓),這樣就能避免存在遠端程式碼執行執行(RCE)漏洞。前面提到過,我們可以在 mailbox
引數中使用某些標誌,其中 /norsh
標誌可以用來禁用IMAP預身份認證模式。
此外,為了避免攻擊者繞過 disable_functions
選項,我們可以將 php.ini
檔案中 imap.enable_insecure_rsh
選項的值設定為 0
。然而在PHP 5中並不能使用這個選項,因此我們應該慎重考慮,判斷是否需要使用 imap
擴充套件,是否需要將 imap_open
新增到禁用函式列表中。