A few months ago, our CTO and hacker-in-chief, Jake Reynolds, bought a glucometer online along with all the necessary stuff to make it work. He thought it would make for an interesting project, as researching this device and its related infrastructure could help improve security in a worthwhile field: health / medical devices. During a slower week at the office, I took a stab at it.
The device Jake purchased was “Contour Next One”. They’ve patched the vulnerabilities by now – you can purchase one here.
Initially, I was interested in tampering with the Bluetooth communication between the device and the phone. After setting up the mobile application and proxying web traffic between the mobile app and the backend, my intuition told me that I could go down a different path. Rather than impacting users within Bluetooth range, how about compromising every user using this device around the world?
This glucometer utilized a mobile application to synchronize data from the hardware device (the glucometer) to the cloud. Through five, low or medium-severity vulnerabilities, we were able to compromise every user’s data on the platform. Moreover, theoretically, we were able to modify any user’s data. We used two test accounts for proof of concepts to avoid tampering with real data. By induction, the proof of concepts apply to any user account.
Note that the data tampered with for the proof of concepts changed minor elements like gender, last updated time, etc… There are other requests that are made to synchronize items like glucose level readings, but those were not part of the proof of concepts.
The impact here is that a malicious entity would have been able to modify any user’s stored glucometer readings over time. If this person’s data is relied upon by medical staff, it could cause some serious issues with predicting and managing their medical prognosis.
Bypass Certificate Pinning
Bypassing certificate pinning on the mobile application was the first step. Certificate pinning was implemented, but the implementation was rather weak. This constituted the first low-severity vulnerability (CVE-2018-18975). It was possible to proxy communications between the mobile application and the backend servers using a jailbroken iPhone (or rooted Android device) and Burp Suite’s Mobile Assistant. At this point, I was able to look at the requests freely.
Two test accounts were created (qwerty1661 and qwerty1662) to avoid tampering with real data. Their user IDs were noted during account creation.
Enumerate Other Users’ Encrypted Data
It was observed that the application made the following request containing a numeric “userID” parameter. The response was encrypted unfortunately. However, changing the user ID number returned a different, encrypted blob of text. I hypothesized that if I could decrypt these two responses, they would reveal two different users’ information. Insecure direct object references (IDORs) is a relatively common web application vulnerability. The impact is typically high if you can view other users’ information but we still had a couple of things to do before we could claim that. Enumerating other users’ data via IDORS constituted the second vulnerability (CVE-2018-18976).
Figure 1: Request/Response Pair
Figure 2: Different User ID Elicits Different Response
At this point, it became necessary to figure out the decryption key and methods for the response. A more in-depth inspection of the iOS binary itself was required. Grepping through the binary for the string “decrypt” matched something in the .ipa file. That was convenient.
Figure 3: Searching for “decrypt” Returns a Match in Native iOS Executable
Unzipping this file showed usage of HTML and JavaScript, indicating that some hybrid web and native code was being used. Searching this directory for “decrypt” indicated a match in the “Onyx” executable. Reverse engineering this iOS executable would have been more painful than reversing the Android application, since we would be dealing with assembly for iOS, as opposed to “obfuscated” Android Java code.
We installed the mobile app on a rooted Android device to attempt obtaining application code directly. We extracted the package and used a decompiler to obtain obfuscated Java source code. Uncovering the obfuscation scheme was rather trivial, thereby constituting another low-severity vulnerability (CVE-2018-18977). Searching for the same ”decrypt” string returns a handy class with two methods that seem promising.
Figure 4: Obfuscated Methods “a” and “b” Along with Cipher Type
Logging was heavily implemented in the program. At this level of obfuscation, these log strings practically explained every method inspected. This allowed us to discover a file containing a plethora of usages of two seemingly random keys.
Figure 5: Two Strings Likely Candidates for AES-CBC-PK512 Key and IV
A sound hypothesis at this point would be that these two strings were the encryption key and initialization vector, both statically coded. In fact, we proved that they were, because we reverse engineered the methods and what parameters they took.
Great, so now that we had the encryption scheme, the statically coded encryption key, and the statically coded initialization vector (constituting two other low-severity vulnerabilities, CVE-2018-18978 and CVE-2018-18979), we wrote simple Python and Java PoCs to decrypt and encrypt data. Manual data encryption will be useful for the next proof of concept.
Crypto implementation was a big factor in decrypting data. Note the usage of UTF-16LE in the encryption and decryption process. I’d also like to thank Cory Shay and Daniel Lawson for coming up with Java and Python code, respectively. One of the coolest parts about working at Depth is being surrounded by some of the most badass hackers I’ve met, all working as one unit. During our weekly “Show ‘n’ Shell”, I explained the situation, then I had a working decryption program in less than 30 minutes. #TealTeamSix
Prior to that, I had been banging my head on the keyboard, trying to figure out why I wasn’t able to decrypt the previously seen messages. There were two primary issues.
Issue #1: I tried sticking to Java to closely replicate the Android code. Python is great. Java is not.
Issue #2: It turned out that I needed to update my Java development kit to obtain the extended cryptography feature set that includes AES/CBC/PKCS5Padding for the given encryption key length and initialization vector length. Otherwise, it was not possible to compile the program, because the older methods didn’t support the given key and IV lengths.
The following Java snippet would decrypt response strings from the Contour web server. I used the Python and Java decryptor/encryptor interchangeably, so you might notice screenshots of a Python program running instead.
[code]
package com.company;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class Main {
public static void main(String[] args) throws Exception {
String response = “ResponseGoesHere”
byte[] b64decodedResponse = Base64.getDecoder().decode(response);
byte[] key = “<decryption_key>”.getBytes(“UTF-16LE”);
byte[] iv = “<iv>”.getBytes(“UTF-16LE”);
Cipher localCipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);
localCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, “AES”), new IvParameterSpec(iv));
byte[] decrypted = localCipher.doFinal(b64decodedResponse);
String ds = new String(decrypted, “UTF-16LE”);
System.out.print(ds);
}
}
[/code]
Proof of Concept I: User Enumeration and Private Data Retrieval
Alright, so we knew we could encrypt and decrypt communications. Let’s put all of that to use.
As mentioned previously, we created two user accounts. User 1 (qwerty1661) sets up their profile and meter while user 2 (qwerty1662) simply creates an account without pairing a meter.
Figure 6: First Response Decrypted
Figure 7: Second Response Decrypted
Above, we extracted two different users’ information using one account. By induction, we could iterate through the entire User ID number space and extract everyone’s information. Of course, this was not done to protect the integrity of patients’ data.
Proof of Concept II: User Data Tampering
So now that we can observe the data, let us modify it. Here, we modified another user’s data while logged in as a different in user. We used the “Upload Data” request to perform this. Since we knew how to decrypt the information, we could now encrypt it by reversing the process. This request was made from User 2 (qwerty1662) after signing in.
Figure 8: Request Made to “UploadUserData”
This request was first decrypted to utilize as a template. We used this template to modify User 1’s data. Let’s grab User 1’s current data.
Figure 9: User 1 (qwerty1661) Gender Was Set to ‘F’, We’ll Change This to ‘M’
We created an encryption program in Java that was the reverse of the decryption process but utilized the same key and IV. We wanted to change User 1’s gender to ‘M’ for this proof of concept.
Figure 10: Encryption Java Program
We changed the ‘F’ to ‘M’, then ran the program to generate the encrypted text. We then replaced the body of the “Upload Data” POST request with the modified, encrypted information for User 1. This request was sent from User 2’s session.
Figure 11: Upload Was Successful
Let us now retrieve User 1’s data through User 2, through the first proof of concept. We grabbed the encrypted text and decrypted it. Indeed, the ‘F’ changed to ‘M’. We did need to change the “LastUpdatedDate” to something more recent for the backend to accept our change, however.
Figure 12: Gender Code Modified from ‘F’ to ‘M’
Conclusion
The point of contact at the company responded quickly, fixed the vulnerabilities within the 90 day responsible disclosure time period, and sent an email to users of the platform informing them of the issue and fix. This was honestly one of the more mature and professional transactions I’ve encountered/seen when it comes to information security research. We desperately need more of these types of transactions in InfoSec. The vendor did the right thing, expediently.
Timeline
October 15, 2018 – Vulnerabilities Responsibly Disclosed
November 5, 2018 – Response Accepting Vulnerabilities
December 5, 2018 – Vendor Informs Userbase of Vulnerability and Fix
January 15, 2019 – 90 Day Responsible Disclosure Period Over
February 14, 2019 – Public Disclosure via Blo