|
Message-ID: <20161228022414.GA9106@openwall.com> Date: Wed, 28 Dec 2016 03:24:14 +0100 From: Solar Designer <solar@...nwall.com> To: oss-security@...ts.openwall.com Cc: marcus@...chromedia.co.uk Subject: Re: PHPMailer < 5.2.18 Remote Code Execution [updated advisory] [CVE-2016-10033] Dawid, That's another nice find of yours, thanks! Going forward, please just "reply" to the same thread whenever you want to share an updated advisory. As you realized, having a new thread means that some people reading the old thread only won't find the new. Now, I think the fix might be incomplete: On Tue, Dec 27, 2016 at 09:45:48AM -0200, Dawid Golunski wrote: > The parameters include the 5th parameter of $params which allows to pass extra > parameters to sendmail binary installed on the system as per PHP documentation > of mail() function: > > http://php.net/manual/en/function.mail.php > > As can we see from: > > $params = sprintf('-f%s', $this->Sender); > > PHPMailer uses the Sender variable to build the params string. [...] > The vulnerability was responsibly disclosed to PHPMailer vendor. > The vendor released a critical security release of PHPMailer 5.2.18 to fix the > issue as notified at: > > https://github.com/PHPMailer/PHPMailer/blob/master/changelog.md > > https://github.com/PHPMailer/PHPMailer/blob/master/SECURITY.md The fix appears to be in this commit: https://github.com/PHPMailer/PHPMailer/commit/4835657cd639fbd09afd33307cef164edf807cdc The code becomes: if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { $params = sprintf('-f%s', escapeshellarg($this->Sender)); } PHP documentation for mail() says this about the 5th parameter: "This parameter is escaped by escapeshellcmd() internally to prevent command execution. escapeshellcmd() prevents command execution, but allows to add additional parameters. For security reasons, it is recommended for the user to sanitize this parameter to avoid adding unwanted parameters to the shell command." So now we effectively have escapeshellcmd(escapeshellarg()). Is this combination meant to be safe? Maybe escapeshellcmd()'s escaping of backslashes will stop them from being treated as escape characters for the single quotes escaped by escapeshellarg()? PHPMailer itself uses both of these functions elsewhere, but separately, like this: if (!empty($this->Sender)) { if ($this->Mailer == 'qmail') { $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); } else { $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); } } else { if ($this->Mailer == 'qmail') { $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail)); } else { $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail)); } } I guess this code runs when PHPMailer does not use mail(). And the code path leading to mail() is separate. But I did not study this in detail. Anyway, my point is that escapeshellcmd(escapeshellarg()) is something new to PHPMailer. Let's see how it behaves: $ cat phpmailer.php #!/usr/bin/php <?php $from = "\"from ' -Xstuff\"@host.tld"; print "From is $from\n"; $arg = escapeshellarg($from); print 'From is ' . $arg . " after escapeshellarg()\n"; $cmd = escapeshellcmd($arg); print 'From is ' . $cmd . " after escapeshellcmd(escapeshellarg())\n"; #system('/bin/echo From is ' . $cmd); mail('root@...alhost', '', '', '', '-f' . $arg); ?> $ env - strace -fe execve ./phpmailer.php execve("./phpmailer.php", ["./phpmailer.php"], [/* 0 vars */]) = 0 >From is "from ' -Xstuff"@host.tld >From is '"from '\'' -Xstuff"@host.tld' after escapeshellarg() >From is '\"from '\\'' -Xstuff\"@host.tld\' after escapeshellcmd(escapeshellarg()) Process 16698 attached [pid 16698] execve("/bin/sh", ["sh", "-c", "/usr/sbin/sendmail -t -i -f'\\\"fr"...], [/* 0 vars */]) = 0 [pid 16698] execve("/usr/sbin/sendmail", ["/usr/sbin/sendmail", "-t", "-i", "-f\\\"from \\", "-Xstuff\"@host.tld'"], [/* 3 vars */]) = 0 sendmail: fatal: unsupported: -Xs I ran this test on a RHEL6'ish and on a RHEL7'ish system, with their packages of PHP, and the result is the same. As you can see, /usr/sbin/sendmail (in this case Postfix's, which is why it isn't accepting "-X") is being run with "-Xstuff\"@host.tld'" as a separate argument. (There's also some escaping by strace in this output. But all we care about is that it's a separate argument, which strace makes clear.) Now, can we get a single quote character through PHPMailer's $this->validateAddress($this->Sender)? I did not test, but the regexps included in there do list it among the allowed characters in some places. There's also the potential (risk) that this code would be run with $patternselect == 'noregex', which does almost no validation. (And if there's no such potential for some reason, then the code handling 'noregex' should simply be dropped. Not good to keep insecure hopefully dead code.) I didn't intend to look into this issue for real, so I'll hand it over back to you from this point on. Please either show how the fix is sufficient, or confirm that it's indeed insufficient. Either way, I think a more appropriate fix would be to implement a trivial SMTP client in PHPMailer and have it talk to 127.0.0.1:25. Of course, there's also the risk of SMTP command injection, so care should be taken to avoid that, yet it's a better defined protocol and the impact of possible injections would be less (unless they exploit a vulnerability in the SMTP server, but having that would be an issue on its own). Failing that, and as another short-term workaround, a stricter sanity check may be applied to the "Sender" address (and maybe to other addresses as well). Perhaps much stricter. Unfortunately, this will disallow use of some obscure valid-per-RFC addresses, but that's still a good tradeoff given the risks. Escaping is OK for trusted user input. For untrusted and possibly malicious input, it just doesn't provide sufficient assurance. Maybe PHP documentation should be revised to introduce this distinction in its descriptions of the escaping functions and their intended use (for SQL escaping, too, where escaping isn't as safe as prepared statements). As the documentation currently is, it gives the impression that escaping is somehow sufficient and is a best practice as the only safety measure for untrusted input. Alexander
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.