前言php
在咱們 挖掘PHP應用程序漏洞 的過程當中,咱們向著名的Webmail服務提供商 Roundcube 提交了一個遠程命令執行漏洞( CVE-2016-9920 )。該漏洞容許攻擊者經過利用Roundcube接口發送一個精心構造的電子郵件從而在目標系統上執行任意命令。在咱們向廠商提交漏洞,發佈了相關的漏洞分析文章後,因爲PHP內聯函數mail()致使的相似安全問題在其餘的PHP應用程序中陸續曝出。在這篇文章中,咱們將分析一下這些漏洞的共同點,那些安全補丁仍然存在問題,以及如何安全的使用mail()函數。html
淺析PHP的mail()函數web
PHP自帶了一個內聯函數mail()用於在PHP應用程序中發送電子郵件。開發者能夠經過使用如下五個參數來配置郵件發送。正則表達式
http://php.net/manual/en/func...shell
bool mail( string $to, string $subject, string $message [, string $additional_headers [, string $additional_parameters ]]
這個函數的前三個參數這裏就不細說了,由於這些參數通常狀況下不會受到注入攻擊的影響。可是,值得關注的一點是,若是$to參數由用戶控制控制的話,那麼其能夠向任意電子郵件地址發送垃圾郵件。安全
郵件頭注入服務器
在這篇文章中咱們重點分析後兩個參數。第四個參數$additional_headers的主要功能是規定額外電子郵件報頭。好比From、Reply-To、Cc以及Bcc。因爲郵件報頭由CRLF換行符rn分隔。當用戶輸入能夠控制第四個參數,攻擊者可使用這些字符(rn)來增長其餘的郵件報頭。這種攻擊方式稱爲電子郵件頭注入(或短電子郵件注入)。這種攻擊能夠經過向郵件頭注入CC:或BCC:字段形成發送多封垃圾郵件。值得注意的是,某些郵件程序會自動將n替換爲rn。函數
爲何沒有正確處理mail()函數的第5個參數會引起安全問題編碼
爲了在PHP中使用mail()函數,必須配置一個電子郵件程序或服務器。在php.ini配置文件中可使用如下兩個選項:url
1.配置PHP鏈接的SMTP服務器的主機名和端口
2.配置PHP用做郵件傳輸代理(MTA)的郵件程序文件路徑
當PHP配置了第二個選項時,調用mail()函數的將致使執行配置的MTA(郵件傳輸代理)程序。儘管PHP內部能夠調用escapeshellcmd()函數防止惡意用戶注入其餘的shell命令,mail()函數的第5個參數$additional_parameters容許向MTA(郵件傳輸代理)中添加新的程序參數。所以,攻擊者能夠在一些MTA中附加程序標誌,啓用建立一個用戶可控內容的文件。
漏洞演示代碼
mail("myfriend@example.com", "subject", "message", "", "-f" . $_GET['from']);
在上述代碼中存在一個遠程命令執行漏洞,這個問題容易被沒有安全意識的開發人員忽略。GET參數徹底由用戶控制,攻擊者能夠利用該處輸入向郵件程序傳遞其餘額外的參數。舉例來講,在發送郵件的過程當中可使用-O參數來配置發送郵件的選項,使用-X參數能夠指定日誌文件的位置。
概念性驗證(PoC)
example@example.com -OQueueDirectory=/tmp -X/var/www/html/rce.php
這個PoC的功能是在Web目錄中生成一個PHP webshell。該文件(rce.php)包含受到PHP代碼污染的日誌信息。所以,當訪問rce.php文件時,攻擊者可以在Web服務器上執行任意PHP代碼。讀者能夠在 咱們的發佈的文章 和 這裏 找到更多關於如何利用這個漏洞的相關信息。
最新相關的安全漏洞
在許多現實世界的應用程序中,有不少因爲mail()函數的第五個參數使用不當引起的安全問題。最近發現如下廣受關注的PHP應用程序受到此類漏洞的影響(多數漏洞由Dawid Golunski發現)。
因爲一些普遍使用的Web應用程序(如 Wordpress, Joomla和 Drupal)部分模塊基於以上庫開發,因此也會受到該類漏洞的影響。
爲何escapeshellarg()函數沒有那麼安全?
PHP提供了 escapeshellcmd() 和 escapeshellarg() 函數用來過濾用戶的輸入,防止惡意攻擊者執行其餘的系統命令或參數。直觀來說,下面的PHP語句看起來很安全,而且防止了-param1參數的中斷:
system(escapeshellcmd("./program -param1 ". escapeshellarg( $_GET['arg'] )));
然而,當此程序有其餘可利用參數時,那麼這行代碼就是不安全的。攻擊者能夠經過注入"foobar' -param2 payload "來突破-param1參數的限制。當用戶的輸入通過兩個escapeshell*函數的處理,如下字符串將到達system()函數。
./program -param1 'foobar'\'' -param2 payload '
從最終系統執行的命令能夠看出,兩個嵌套的轉義函數混淆了引用並容許附加另外一個參數param2。
PHP的mail()函數在內部使用escapeshellcmd()函數過濾傳入的參數,以防止命令注入攻擊。這正是爲何escapeshellarg()函數不會阻止mail()函數的第5個參數的攻擊。 Roundcube和 PHPMailer的開發人員率先發布了針對該漏洞的補丁。
爲何FILTER_VALIDATE_EMAIL是不安全的?
另外一種直接的方法是使用PHP的電子郵件過濾器(email filter),以確保在mail()函數的第5個參數中只使用有效的電子郵件地址。
filter_var($email, FILTER_VALIDATE_EMAIL)
可是,並非全部可能存在安全問題的字符串都會被該過濾器過濾。它容許使用嵌入雙引號的轉義的空格。
因爲函數底層實現正則表達式的緣由,filter_var()沒有對輸入正確的過濾,致使構造的payload被帶入執行。
'a."' -OQueueDirectory=%0D<?=eval($_GET[c])?> -X/var/www/html/"@a.php
對於上文給出的url編碼輸入,filter_var()函數返回true,將該payload識別爲有效的郵件格式。
當開發人員使用該函數驗證電子郵件格式做爲惟一的安全驗證措施,此時仍然是能夠被攻擊者利用的:與咱們以前的攻擊方式相似,在PHP程序發送郵件時,咱們精心構造的惡意「電子郵件地址」會將將PHP webshell生成在Web服務根目錄下。
<?=eval($_GET[c])?>/): No such file or directory
切記,filter_var()不適合用於對用戶輸入內容的過濾,由於它對部分字符串的驗證是不嚴格的。
如何安全的使用mail()函數
仔細分析應用程序中傳入mail()函數的參數,知足如下條件:
$to 除非能夠預期用戶的輸入內容,不然不直接使用用戶輸入
$subject 能夠安全的使用
$message 能夠安全的使用
$additional_headers 過濾r、n字符
$additional_parameters 禁止用戶輸入
事實上,當把用戶的輸入做爲shell指令執行時,沒有什麼辦法能夠保證系統的安全性,千萬不要去考驗你的運氣。
若是在開發您的應用程序過程當中第5個參數必定要由用戶控制,你可使用電子郵件過濾器(email filter)將用戶輸入的合法數據限制爲最小字符集,即便它違反了RFC合規性。咱們建議不要信任任何轉義或引用程序,由於據歷史資料表示 這些功能是存在安全問題的,特別是在不一樣環境中使用時,可能還會暴露出其餘安全隱患。Paul Buonopane研究出了另外一種方法去解決這個問題,能夠在 這裏找到。
總結
許多PHP應用程序都有向其用戶發送電子郵件的功能,例如提醒和通知。 雖然電子郵件頭注入是衆所周知的安全問題,可是當開發人員使用mail()函數時,每每會忽視不正當的使用有可能致使遠程命令執行漏洞。 在這篇文章中,咱們主要分析了mail()函數的第5個參數使用不當可能存在的安全風險,以及如何防範這種問題,防止服務器受到攻擊。
本文由甲爪cpa聯盟(www.jiazhua.com)整理編輯!