Monthly Archives: December 2017

Intercepting HTTPS Traffic from Apps on Android 7+ using Magisk & Burp

Intercepting HTTPS traffic is a necessity with any mobile security assessment. By adding a custom CA to Android, this can easily be done. As of Android Nougat, however, apps don’t trust client certificates anymore unless the app explicitly enables this. In this blogpost, we present a new Magisk module, that circumvents this requirement, by automatically adding client certificates to the system-wide trust store, which is by default trusted by all apps.

Basic HTTPS interception

Intercepting HTTPS on Android is a very straight-forward job, which only takes a few steps:

  1. Set Burp as your proxy
  2. Visit http://burp
  3. Install the Burp certificate as a user certificate
  4. Intercept

After following these steps, you can view all HTTPS traffic that is sent through the user’s browser. A more detailed explanation can be found on Portswigger’s website.

In the past, this approach would even work for app traffic as the application would trust all installed user certificate by default. One way to prevent app traffic from being intercepted, is by installing certificate pinning. Certificate pinning means that on each SSL connection the certificates presented by the server will be compared to a locally stored version. The connection will only succeed if the server can provide the correct identity. This is a great security feature, but can be tricky to implement.

Enter Android Nougat

Starting with Android Nougat, apps no longer trust user certificates by default. A developer can still choose to accept user certificates by configuring the networkSecurityConfig attribute in the app’s AndroidManifest.xml file, but by default, they are no longer trusted.

A first approach would be to decompile, modify and recompile the application, which are quite some steps to perform. If the app turns out to have protection against repackaging files, this would also be very difficult. An example of this technique can be found on warroom.securestate.com.

A different approach is adding the user certificate to the system store. The system store is located at /system/etc/security/cacerts and contains a file for each installed root certificate.

Screen Shot 2017-12-15 at 16.07.58A very simple solution would be copying the user installed file (found at /data/misc/user/0/cacerts-added) to this folder. This is only possible, however, if the system is mounted as r/w. Now, while it is possible to remount /system and perform the necessary actions, this is a rather dirty solution and some root-detection algorithms will detect this modification.

Using Magisk

Magisk is a “Universal Systemless Interface, to create an altered mask of the system without changing the system itself.” The fact that Magisk doesn’t modify the /system partition makes it a very nice solution for security assessments where the application has enhanced root detection. By activating “Magisk Hide” for the targeted application, Magisk becomes completely invisible.

Magisk also supports custom modules that are fairly easy to create. In order to have any user certificate recognized as system certificates, we made a simple Magisk module which can be found on our github. The logic of the module is very basic:

  1. Find installed user certificates
  2. Add them to the /system/etc/security/cacerts directory

When installed, the content of the Magisk module is mounted on /magisk/trustusercerts/. This folder contains multiple files, but the most important one is the system directory. This directory is automatically merged with the real /system directory, without actually touching the /system partition. As a result, all certificates in /magisk/trusteusercerts/etc/security/ will end up in /system/etc/security.

Using the module is easy:

  1. Install the module
  2. Install certificates through the normal flow
  3. Restart your device

After installation, the certificates will show up in your system wide trust store and will be trusted by applications:

 

 

Of course, if the application has implemented SSL Pinning, you still won’t be able to intercept HTTPS traffic, but this little module makes Android Nougat apps perform the same way as pre-Android Nougat apps.

If you have any suggestions on how to improve this module, or any ideas on how to add the ability to disable SSL Pinning on the Magisk level, let us know!

Download Magisk Module from Github

About the author

AAEAAQAAAAAAAAYHAAAAJGUzZmUxMmVmLWY3M2MtNDRmNy05YzZlLWMxZTk1ZTE5MWYzMQ
Jeroen Beckers is a mobile security expert working in the NVISO Cyber Resilience team. He also loves to program, both on high and low level stuff, and deep diving into the Android internals doesn’t scare him. You can find Jeroen on LinkedIn.

Hack Our Train

This year, in an effort to raise awareness about IoT security, we launched the Hack Our Train challenge. For over three weeks, a model train tirelessly chugged on its tracks inside our IoT village at Co.Station Brussels and then once more for two days at BruCON 2017. We provided it with an emergency brake system powered by IoT technologies and challenged people to hack it and stop the train! With every successful hack, the train stopped, creating a service disruption. A live-stream allowed hackers to monitor the effects of their attacks.

The Hack Our Train challenge was actually composed of two parts: a local one, situated close to the IoT village and then its online counterpart. The local challenge did not require any specific technical skills. It invited people to try and break the pin of the controller that activates the emergency brake mechanism. Check out this video of people having fun with the controller:

But, the online part is where things became really interesting! Over the course of the challenge, only a handful of people succeeded in figuring it out and remotely stopped the train… In this post, we’ll provide a walk-through of the challenge and focus on some of the vulnerabilities featured in it and, more importantly, on ways to fix them.

Hunt for the firmware

On the challenge website, we provided aspiring hackers with the following scenario: Bob, one of the IoT village’s railroad technicians, recently had to update the emergency brake panels. Unfortunately Bob left his USB flash drive laying around and Eve found it and made its content available online!

The first step is to analyze the USB’s contents. It is a zip archive containing two files: firmware.enc, which conveniently hints to the encrypted firmware and a binary file called firmware_updater. The firmware updater is used to decrypt the firmware and then upload it to the control panel via a serial connection.

If we execute it on a Linux machine, the firmware_updater asks us for a pin before decrypting the firmware. People who braved the challenge came up with all sorts of clever ways of cracking the firmware_updater binary and forcing it to decrypt the firmware.

For now, we will resist the temptation of breaking out our dissassemblers and debuggers and we will just look at the strings inside the updater. There are some really interesting parts:

strings1

strings2

If we think this out (of course, dissassembling would make this much easier), the firmware seems to be encrypted using AES. To perform the decryption ourselves, we would need the key and the initialization vector (IV). Luckily, these are hard-coded into the firmware (see image above).

So, we just need to turn those into their hexadecimal counterparts and we are set:

> openssl enc -aes-128-cbc -d -in firmware.enc -out firmware -iv 766563353667647639396e6e73647161 -K 686f746b3138313965663132676a7671

Vulnerability: use of hardcoded cryptographic keys.

Fix: if possible, avoid using schemes that require hardcoded keys. If using a hardcoded key is unavoidable, there are techniques that can be employed to make the key harder to recover. Ideally, decryption should be performed directly on the embedded device, which avoids the need to expose the key in the firmware updater.

One last word on the update procedure we just cracked: now that we have the keys, no one can stop us from modifying the decrypted firmware to add or remove some functionalities, encrypting it again and uploading it to the device. The emergency controller would have no means of knowing if the firmware has been tampered with.

Vulnerability: firmware not digitally signed.

Fix: Digitally sign the firmware so that the embedded device can verify its authenticity.

Inside the firmware

Phew, we have the firmware, now comes the hard part: we have to reverse engineer it and find a way of remotely trigger the emergency brake mechanism.

Using the file command on the firmware reveals it is in fact a Java archive. This is really good news: using a Java decompiler, we can easily recover the source code.

Vulnerability: code is easy to reverse engineer. Attackers have easy access to the internal workings of the application, which makes it easier for them to find exploits.

Fix: use a code obfuscation tool – there are many available online, both free and commercial. Once you have used the tool on your code, test your application to make sure that it is still functioning correctly. Remember that obfuscation is not a silver bullet but it can drastically increase the effort required for an attacker to break the system.

Before looking at the code, let’s try running the firmware. We are greeted with a status page containing a button to initiate an emergency brake, but we need a pin.

screen1screen2

A quick look inside the source code reveals that it is simply “1234”. We have managed to unlock the button. Spoiler: it doesn’t work!

Vulnerability: use of hardcoded passwords.
In our scenario, the password was of no immediate use. Still, this would be harmful if we ever had access to a real emergency brake controller.

Fix: if the password must be stored inside the application, it should at least be stored using a strong one-way hash function. Before hashing, the password should be combined with a unique, cryptographically strong salt value.

In order to understand what is going on, we can simply look at the debug messages conveniently left behind in the console. Seems like the protocol used is MQTT and for some reason, we receive an authentication failure error when we try to perform an emergency brake.

Vulnerability: leftover debug messages containing useful information.

Fix: scan your code for forgotten prints (system.outs in our case) and remove them before release, or disable them at runtime.

Look below the hood

Before going further, let’s take a step back and have a closer look at the architecture of our system. This step was of course not needed to solve the challenge but we thought it complements nicely to this walk-through!

As we just discovered by looking at the source code, communication is based on the MQTT protocol, often found among IoT applications. MQTT works on top of TCP/IP and defines two kinds of entities: Subscribers and Publishers. Publishers send messages on communication channels called Topics. Subscribers can register and read messages on specific Topics. These messages are relayed with the help of a server (also called a Broker).

circuit

This is a look below the hood of the mountain (this was our beta setup, our final circuit was much cleaner!). Two elements steal the show: an Arduino and a Raspberry Pi. The Arduino is the muscle: it controls the transistor that stops the train. The Pi is the brains: when it receives an emergency brake message, it orders the Arduino to stop the train.

Both the emergency brake controller (where the firmware runs – it is not shown in the picture) and the Raspberry Pi (shown in the picture) are connected to the internet. They communicate with the MQTT Broker to exchange MQTT messages. The Pi publishes messages concerning the train’s speed but it is also subscribed to a topic waiting for emergency brake messages. The controller displays the train’s current speed and, when the emergency brake is activated, it publishes an emergency brake message that the Broker relays to the Pi.

Of course, not anyone can send an emergency brake message to the server. In our infrastructure, authentication is based on JWT tokens. These are issued by the server relaying the MQTT messages and they are signed using the server’s private key. When  clients try to authenticate using one of those tokens, the server can verify their authenticity using its public key.

To clear all this up, we have created an overview of the MQTT communications going on in the images below:

speed

brake

Tokens, tokens everywhere…

Back the authentication problem. Digging into the source code confirms that authentication is JWT token based. Maybe there is something wrong with the token? Another file inside the JAR that immediately draws our attention is notes.txt. A quick look reveals some notes of a developer that was worried about his JWT token expiring. We can easily verify the creation date of the token here. Seems like out token is really old and as we don’t have the authentication server’s private key, we cannot create a new one.

Vulnerability: old and unused left behind files containing sensitive information.

Fix: before publishing your product, make sure to remove every non-essential ressource.

Knowing how the authentication works, it is time to turn to our favorite search engine for more intelligence. Let’s try jwt token vulnerability. The top result states “Critical vulnerabilities in JSON Web Token libraries“: perfect!

The author does a great job of explaining the vulnerability and how it can be exploited, so we leave you in his hands. For the purposes of this post, the idea is that if we create a forged token using the authentication server’s public key as a HMAC secret key, we can fool the server into verifying it using HMAC instead of RSA, which results in the server accepting our token.

Attack!

Having identified the vulnerability, it is time to perform our attack. For that, we need the server’s public certificate, which is kindly included in the JAR as well. As mentioned in the notes file, it has been converted to a format compatible with Java but the server is more likely using it in PEM format.

Luckily, converting it back is easy:

> keytool -importkeystore -srckeystore hot.ks -destkeystore temp.p12 -srcstoretype jks -deststoretype pkcs12
> openssl pkcs12 -in temp.p12 -out public.pem

 

Next, we have to create our malicious token. You can do this in the language of your choice. We used the jwt-simple node.js module.

With the malicious token crafted, we can finally perform the attack. The easiest way is to reuse the code included in the testMQTT.java file, conveniently forgotten inside the JAR. We just have to replace the token found in the code, compile the code and execute it from the terminal.

The select few who made it this far in the challenge saw the train stop on the live-stream and received the flag!

Vulnerability: using components with known vulnerabilities.

Fix: apply upgrades to components as they become available. For critical components (for example, those used for authentication), monitor security news outlets (databases, mailing lists etc.) and act upon new information to keep your project up to date.

Lessons learned

Our challenge involved a toy train but the IoT vulnerabilities demonstrated inside are the real deal. We added each one of them to the IoT challenge because we have come across them in the real world.

On a final note, we would like to congratulate those who were able to hack our train and we sincerely hope that all of you enjoyed this challenge!