在PHP应用程序开发中不正当使用mail()函数引起的血案

前言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发现)。

clipboard.png

因为一些普遍使用的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()函数的参数,知足如下条件:

  1. $to 除非能够预期用户的输入内容,不然不直接使用用户输入

  2. $subject 能够安全的使用

  3. $message 能够安全的使用

  4. $additional_headers 过滤r、n字符

  5. $additional_parameters 禁止用户输入

事实上,当把用户的输入做为shell指令执行时,没有什么办法能够保证系统的安全性,千万不要去考验你的运气。

若是在开发您的应用程序过程当中第5个参数必定要由用户控制,你可使用电子邮件过滤器(email filter)将用户输入的合法数据限制为最小字符集,即便它违反了RFC合规性。咱们建议不要信任任何转义或引用程序,由于据历史资料表示 这些功能是存在安全问题的,特别是在不一样环境中使用时,可能还会暴露出其余安全隐患。Paul Buonopane研究出了另外一种方法去解决这个问题,能够在 这里找到。

总结

许多PHP应用程序都有向其用户发送电子邮件的功能,例如提醒和通知。 虽然电子邮件头注入是众所周知的安全问题,可是当开发人员使用mail()函数时,每每会忽视不正当的使用有可能致使远程命令执行漏洞。 在这篇文章中,咱们主要分析了mail()函数的第5个参数使用不当可能存在的安全风险,以及如何防范这种问题,防止服务器受到攻击。
本文由甲爪cpa联盟(www.jiazhua.com)整理编辑!

相关文章
相关标签/搜索