CM Browser: HTTPS URL Leak

This blog post presents a vulnerability which affects the widely installed Android web browser.

Introduction

CM Browser is a well-known web browser developed by the chinese company Cheetah Mobile available for free on the Play Store. At the time of writing, the application has been installed over 50 million times according to the store [1] [2]. Reading through its description, this web browser provides a bunch of security-oriented features such as malware scanning after downloading a file or phishing detection while browsing online. However, anti-phishing mechanisms seem to be performed from server side hence, we would like to know how they are actually working. The most efficient way to do so is to have a look at the implementation.

Technical aspects

After firing up the web browser on a device and accessing a few random web pages, we quickly notice that some HTTP requests are sent on a regular basis, to a web server seemingly owned by the China-based company that develops the application. This mechanism occurs whenever we are loading a new web page — thus, these events are likely linked somehow. Even though HTTP is a cleartext protocol, data embedded in these requests is not human readable so we can't figure out what the application is actually sending to the server. At this point, we assume that the application implements an overlying encryption layer within its code.

In order to know what data is transmitted during this communication, we have to look into the application's code part responsible for handling that specific request. After finding the right method statically, Frida helps us print input parameters and return value during runtime. In the first time, it allows us to get outgoing data before encryption, and thus find out what kind of information is sent to the server. Below is an example of the byte sequence transmitted once searching on Google (represented as Python bytes).

b'\x0186d6ad3db13231aa\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00204400603d1a76be=\x00\x00\x00\x00\x00\x00\x00X\x00https://www.google.com/search?ie=UTF-8&source=android-browser&q=who+s+watching+me'

We promptly notice some ASCII parts which look like identification tokens — they are in the form of hashes. More interestingly, we also observe the full URL at the end of the byte sequence. Full URL means scheme, host, port but also query part such as HTTP GET parameters. For instance, getting back to the previously mentioned byte sequence, we can identify what the user is looking for thanks to the parameter named q.

Now, let's move on and see how this byte sequence is encrypted before sending it over to the remote web server. Thanks to Frida again, we are able to easily locate the method responsible for data encryption (and decryption used for HTTP response from web server as well) and print parameters and output value. The method's logic is pretty straightforward as shown below in Java representation.

/* method called for both encrypting and decrypting data */
public static byte[] a(byte[] bArr /* data */, byte[] bArr2 /* key */) {
    byte[] bArr3 = new byte[bArr.length];
    for (int i = 0; i < bArr.length; i++) {
        bArr3[i] = (byte) (bArr[i] ^ bArr2[i % bArr2.length]);
    }
    return bArr3;
}

It basically takes a byte array which represents data and a key as input. As a result, it returns either an encrypted or a decrypted byte sequence depending on where the method is called from. The internal process is quite clear: it iterates over the data sequence and applies a XOR operation on each byte. The value the byte is XOR'd with is determined thanks to its position in the byte array. As the XOR operation has the convenient property of being reversible, this method can be used for both encryption and decryption, given the same key. Nevertheless, it also means that, having the encrypted data and knowing the key, one is fully able to get the decrypted data. Here comes the issue. It turns out that the key is hard-coded within application's code and hasn't been changed for a while, not even once across recent versions.

Consequences

This flaw implies that an attacker able to intercept victims' traffic (e.g. Man In the Middle Attack scenario), can passively decrypt those specific requests on-the-fly then spy on victims' web browsing, even over HTTPS. HTTPS aims to add an encryption layer to regular HTTP protocol [3]. Hence, all the decrypted traffic, including URLs, is meant to be accessible only by the two ends (client and server). However, in this case, accessed URLs are also sent via an extra insecure means and can thus be retrieved by a malicious third party. Indeed, as described above, an adversary could easily snoop on what a victim is looking for on Google — it also works on various similar platforms such as Bing, DuckDuckGo and Baidu. Obviously, this vulnerability doesn't solely involve search engines since it allows an attacker to get hold of every URL accessed by victims throughout their web browsing. Moreover, it could be even worse whenever secrets such as tokens and passwords, are passed as HTTP GET parameters. It is worth noting that this flaw affects the so-called incognito mode as well.

Demonstration

This demonstration shows the decryption of every request sent by CM Browser to the remote web server, in real time. In this video, we use the mitmproxy tool suite in order to intercept them. However, in a real-world setting, an adversary has to set up some prior requirements such as MITM attack to eavesdrop on a victim's traffic. Note that the CM Browser's version we are running on is 5.22.21.0051.

Mitigations

No vulnerability patch has been released yet. At this time, the flaw is still present in the latest version of CM Browser available on the Play Store.

Timeline

  • March 29th, 2019: vulnerability discovery
  • April 1st, 2019: first email asking for the email address of the department in charge of security-related issues
  • April 13th, 2019: second reminder email
  • April 29th, 2019: third reminder email
  • May 5th, 2019: last reminder email before public disclosure
  • November 14th, 2019: disclosure

Comments