CVE-2018–9846: Roundcube 1.2.0–1.3.5 mx injection

Andrea Basile
Apr 8, 2018 · 3 min read
What do this path, and the bug I’m writing about, share?

What better way to inaugurate this blog than a post about a subtle-as-low-impact mx vulnerability on Roundcube? With this in mind, let’s pretend to have fun!

PS: I’ll refer to code 1.3.5 where not specified.

The executive summary

Caption not needed: clearly a portrait of a suited up CEH security engineer talking to a manager who couldn’t care less.

Roundcube is a well regarded web mail client, used by many important Institutions/Universities and it’s the default choice of many hosting providers.

Roundcube versions from 1.2.0 to 1.3.5 with the “archive” plugin enabled and configured, suffer from a mx injection vulnerability.

An attacker may exploit this to inject IMAP commands and perform malicious actions, like deleting your emails.

Well, that would do me a favor eheh 15000 unreads and countin’. Do we really have to fix this?!

THE TECHNICAL STUFF: Is it $_POST or is it $_GET?

To answer this big dilemma we need to look at where everything starts: archive.php:113:move_messages()

It’s clearly a $_POST.

Few lines below things get interesting...

First, our goal is to get into the last branch. This means that the archive folder must be a special one💖; in Roundcube the archive should archive according to a listed option (author, date etc). However in version 1.2.0 this requirement isn’t needed.

Why? Simply ‘cause move_message_worker() does its job right. That’s why, luv.

Well, then this happens:

  1. line 176 archive.php:move_messages() calls fetch_headers($mbox, $uids);
  2. line 1235 rcube_imap.php:fetch_headers() calls fetchHeaders($folder,$msgs) where $folder is $mbox and $msgs is $uids;
  3. line 2600 rcube_imap_generic.php:fetchHeaders() calls fetch($mailbox, $message_set, $is_uid, $query_items);
  4. rcube_imap_generic.php:fetch() is a core function used everywhere to do its job: fetching things 🐶.
rcube_imap_generic.php fetch()

$mailbox ($mbox) and $message_set ($uids) are still our guys; hence we get right where we wanted: $request which then, ‘ll be executed.

So what’s the problem? Okay, few “fetch-this” and “fetch-that” but in the end everything runs smoothly.

Well, unfortunately this isn’t exploitable in a real scenario since Roundcube will perform rcube.php:check_request()

Shit happens.

… and our special $uids is passed by $_POST… wait! They are using != and == instead of the strict ones, that’s a type juggling issue right there! Maybe there’s still hope?

Nope, everything is passed with HTTP Parameters: no JSON, no typed stuff.

Wait. Don’t rush. Meditate, open up your chakras…

Let’s go back to the starting point, archive.php, on line 152 calls rcmail::getuids() which does this:

plotwist.

this means we can pass _uids as $_GET, this way check_request() will treat our request like an empty $_POST.

Security check: bypassed✅. Our mx injection is exploitable.

Final Notes

In latest versions, the archive’s plugin accepts just AJAX requests, making it harder to perform the attack, since SOP must be either complied or bypassed.

In version 1.2.0 (likely also 1.2.1 / 1.2.2 or prior— I haven’t checked when the AJAX-only rule was introduced) the attack is straightforward, tricking the victim to click on a link like:

http://victim-roundcube.com/somedir/?_task=mail&_mbox=INBOX&_action=plugin.move2archive&_uid=255%20BODY[HEADER]%0d%0aA0006%20CREATE%20%22HACKED%22%0d%0aA0007%20UID%20FETCH%20255

would be enough.

Andrea Basile

Written by

place where i write stuff about bugs i didn’t manage/care to sell (as if…)

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade