Exploit-DB #41864
Description: Horde Groupware Webmail 3/4/5 - Multiple Remote Code Executions
Type: webapps
Platform: php
Date: 2017-04-11
Author: SecuriTeam

Exploit:
Source: https://blogs.securiteam.com/index.php/archives/3107

Vulnerabilities Summary
The following advisory describes two (2) vulnerabilities found in
Horde Groupware Webmail.

Horde Groupware Webmail Edition is a free, enterprise ready, browser
based communication suite. Users can read, send and organize email
messages and manage and share calendars, contacts, tasks, notes,
files, and bookmarks with the standards compliant components from the
Horde Project. Horde Groupware Webmail Edition bundles the separately
available applications IMP, Ingo, Kronolith, Turba, Nag, Mnemo,
Gollem, and Trean.

It can be extended with any of the released Horde applications or the
applications that are still in development, like a bookmark manager or
a file manager.

Affected versions: Horde 5, 4 and 3

The vulnerabilities found in Horde Groupware Webmail are:

Authentication Remote Code Execution
Unauthentication Remote Code Execution

Credit
An independent security researcher has reported this vulnerability to
Beyond Security’s SecuriTeam Secure Disclosure program.

Vendor response
Horde has released a patch to address the vulnerabilities.

For more information:
https://lists.horde.org/archives/horde/Week-of-Mon-20170403/056767.html

Vulnerabilities Details

Authentication Remote Code Execution
Horde Webmail contains a vulnerability that allows a remote attacker
to execute arbitrary code with the privileges of the user who runs the
web server.

For successful attack GnuPG feature should be enabled on the target
server (path to gpg binary should be defined in $conf[gnupg][path]
setting).

Vulnerable code: encryptMessage() function of GPG feature.

Path: /Horde/Crypt/Pgp/Backend/Binary.php:

/* 416 */ public function encryptMessage($text, $params)
/* 417 */ {
/* … */
/* 435 */ foreach (array_keys($params['recips']) as $val) {
/* 436 */ $cmdline[] = '--recipient ' . $val;
#! vulnerable code
/* … */
/* 444 */ /* Encrypt the document. */
/* 445 */ $result = $this->_callGpg(
/* 446 */ $cmdline,
/* 447 */ 'w',
/* 448 */ empty($params['symmetric']) ? null : $params['passphrase'],
/* 449 */ true,
/* 450 */ true
/* 451 */ );

$params[‘recips’] will be added to $cmdline array and passed to _callGpg():

Path: /Horde/Crypt/Pgp/Backend/Binary.php:

/* 642 */ public function _callGpg(
/* 643 */ $options, $mode, $input = array(), $output = false, $stderr = false,
/* 644 */ $parseable = false, $verbose = false
/* 645 */ )
/* 646 */ {
/* … */
/* 675 */ $cmdline = implode(' ', array_merge($this->_gnupg, $options));
/* … */
/* 681 */ if ($mode == 'w') {
/* 682 */ if ($fp = popen($cmdline, 'w')) {                        #!
vulnerable code
/* … */

We can see that our recipients (addresses) will be in command line
that is going to be executed. encryptMessage() function can be reached
by various API, requests. For example it will be called when user try
to send encrypted message.

Our request for encryption and sending our message will be processed
by buildAndSendMessage() method:
Path: /imp/lib/Compose.php

/* 733 */ public function buildAndSendMessage(
/* 734 */ $body, $header, IMP_Prefs_Identity $identity, array $opts = array()
/* 735 */ )
/* 736 */ {
/* 737 */ global $conf, $injector, $notification, $prefs, $registry, $session;
/* 738 */
/* 739 */ /* We need at least one recipient & RFC 2822 requires that no 8-bit
/* 740 */ * characters can be in the address fields. */
/* 741 */ $recip = $this->recipientList($header);
/* ... */
/* 793 */ /* Must encrypt & send the message one recipient at a time. */
/* 794 */ if ($prefs->getValue('use_smime') &&
/* 795 */ in_array($encrypt, array(IMP_Crypt_Smime::ENCRYPT,
IMP_Crypt_Smime::SIGNENC))) {
/* ... */
/* 807 */ } else {
/* 808 */ /* Can send in clear-text all at once, or PGP can encrypt
/* 809 */ * multiple addresses in the same message. */
/* 810 */ $msg_options['from'] = $from;
/* 811 */ $save_msg = $this->_createMimeMessage($recip['list'], $body,
$msg_options);        #! vulnerable code

In line 741 it tries to create recipient list: Horde parsers values of
‘to’, ‘cc’, ‘bcc’ headers and creates list of Rfc822 addresses. In
general there are restrictions for characters in addresses but if we
will use the next format:

display-name <"somemailbox"@somedomain.com>

somemailbox will be parsed by _rfc822ParseQuotedString() method:

Path: /Horde/Mail/Rfc822.php:

/* 557 */ protected function _rfc822ParseQuotedString(&$str)
/* 558 */ {
/* 559 */ if ($this->_curr(true) != '"') {
/* 560 */ throw new Horde_Mail_Exception('Error when parsing a quoted string.');
/* 561 */ }
/* 563 */ while (($chr = $this->_curr(true)) !== false) {
/* 564 */ switch ($chr) {
/* 565 */ case '"':
/* 566 */ $this->_rfc822SkipLwsp();
/* 567 */ return;
/* 569 */ case "\n":
/* 570 */ /* Folding whitespace, remove the (CR)LF. */
/* 571 */ if (substr($str, -1) == "\r") {
/* 572 */ $str = substr($str, 0, -1);
/* 573 */ }
/* 574 */ continue;
/* 576 */ case '\\':
/* 577 */ if (($chr = $this->_curr(true)) === false) {
/* 578 */ break 2;
/* 579 */ }
/* 580 */ break;
/* 581 */ }
/* 583 */ $str .= $chr;
/* 584 */ }
/* 586 */ /* Missing trailing '"', or partial quoted character. */
/* 587 */ throw new Horde_Mail_Exception('Error when parsing a quoted string.');
/* 588 */ }

There are only a few limitations:

we cannot use “
\n will be deleted
we cannot use \ at the end of our mailbox

After creation of recipient list buildAndSendMessage() will call
_createMimeMessage():

Path: /imp/lib/Compose.php

/* 1446 */ protected function _createMimeMessage(
/* 1447 */ Horde_Mail_Rfc822_List $to, $body, array $options = array()
/* 1448 */ )
/* 1449 */ {
/* 1450 */ global $conf, $injector, $prefs, $registry;
/* ... */
/* 1691 */ /* Set up the base message now. */
/* 1692 */ $encrypt = empty($options['encrypt'])
/* 1693 */ ? IMP::ENCRYPT_NONE
/* 1694 */ : $options[