Performing a MitM attack against an HTTPS channel requires the capability for the attacker to be able to add the proxy server Certificate Authority (CA) into the Trust Store of the device running the mobile app and a popular approach is to manually upload the CA to the device, but this comes with some challenges, that may require to root the device and/or repackage the mobile app.
An easier way exists, and in this article I will show how to use an Android Emulator with a writable file system that will allow us to install the proxy certificate directly into the system trusted store, without the need to root the emulator or make changes in the mobile app.
This is a hands on how to tutorial, that you can easily follow, even if you have not done a MitM attack before or you are just starting your developer Android journey.
In a nutshell, the popular approach requires to have a rooted device or emulator to allow to install the CA directly into the device Trust Store, while the approach taken in this article is to use an emulator that is started and configured with a writable file system without the need to root it. The alternative to not root the device or emulator is to add the CA to the user Trust Store on the device, but this approach doesn't work from Android API 24 onwards, and to circumvent it the mobile app needs to be repackaged with a modification to trust in user provided CA's.
Since Android API 24, the Android OS (operating system) requires mobile apps to explicitly opt-in to trust user provided certificates, as you can read in this announcement. This was an important security improvement to the Android OS in order to prevent regular users from being tricked into installing certificates that would then allow an attacker to MitM every outgoing HTTPs request. Unfortunately it doesn’t prevent determined attackers from bypassing it in a device they control.
So, if the mobile app being attacked works on Android API 23 and below, the popular approach can work without the need to repackage the mobile app, because user provided certificates will be trusted, unless certificate pinning was implemented via a library or custom code.
The approach in this article uses an emulator to allow starting the Android operating system in writable mode, so that we can inject the mitmproxy custom certificate in the system trusted store. This prevents it being necessary to add the certificate to the user trusted store and having to repackage the mobile app to make it trust in user provided certificates, as required since Android API 24. The approach described in this article has the advantage of using the same method to make the custom certificate trusted by the system across any Android API version where it’s possible to start the emulator with a writable file system.
Also, using an emulator with a writable file system gives a lot of power to the attacker to circumvent the security measures in place on the mobile app, and it's not limited to injecting custom certificates; this approach can also be used to install and run additional tools. In a follow-up article we will explore how to install an instrumentation framework to be able to hook into the code at runtime and modify its behavior.
An attacker may want to intercept the communications between your mobile app and your API server in order to collect enough information to automate attacks against it, or just to modify or redirect your data on the fly.
Legitimate users wanting to get around limitations imposed by the mobile app’s user interface to access certain services, where those services are available via the API. Just one of many potential examples would be where the legitimate user of a mobile app may want to gamify or have access to features/resources not available within his/her current subscription plan, or because they want to collect/use more reward points or discount vouchers then they are allowed to or entitled to.
A plethora of open source tools exist to help us with what seems an advanced task only mastered by hackers, pentesters and security researchers.
The good news is that you don’t need to be one of them to be able to perform a MitM attack, you just need to be someone that feels comfortable using computers and be a little tech savvy in order to follow a step by step tutorial.
The approach we are about to see will be limited to succeeding in mobile apps that don’t use certificate pinning and/or runtime self-protection mechanisms.
To bypass certificate pinning in a mobile app you will want to look to the next article that leverages the current approach and adds an instrumentation framework into the mix to hook in the code at runtime to enable the pinning check to always succeed.
This tutorial will be step by step, and we will assume that you will be following it along on a Linux based computer and that you already have Android Studio installed and configured. This specific tutorial has been done on an Ubuntu 20.04 desktop computer, therefore if you are running in another platform you can try to use an Ubuntu 20.04 VM to follow along or adapt the steps to your current platform. Other linux distributions will probably work without any issues with this tutorial too, but haven't been tested.
To prepare your system to follow along this tutorial you need to follow this Github gist for the following steps:
Now that we have finished the setup of all additional requirements, we can start with the fun stuff.
To show how to do a MitM attack on a mobile app we will use the ShipFast mobile app release flavour for the API key demo stage. The ShipFast mobile app is part of this series of articles on practical API security for mobile apps, and it can be found on this Github repo.
First we will use the ShipFast app in the emulator as a regular user. This means we will not attempt to do any type of attack on it. The goal is just to show how the mobile app works in normal circumstances.
Next we will run ShipFast in the emulator that has been modified to include the mitmproxy certificate in its system trusted store and observe how easy it is to intercept the requests to the API backend by proxing them through the mitmproxy.
By following the previously recommend steps for the emulator setup with Android 29 you should already have an instance of the emulator with the boot process completed, and sitting on this screen:
Now follow to the next section to install the ShipFast mobile app.
First, download the ShipFast mobile app with:
curl -LO https://github.com/approov/shipfast-api-protection/releases/download/2.2.0/app-api_key-release.apk
Next, install it on the emulator:
adb install app-api_key-release.apk
Then, launch the app via adb with:
adb shell am start -n com.criticalblue.shipfast.api_key/com.criticalblue.shipfast.LoginActivity
Once the the mobile app is launched you should see this screen:
Now login with Auth0 by using any of the login methods:
After the all the login steps are completed successfully you should see the following:
Authorize the app to access your device location, and then you should be presented with this screen:
Now click on the I’m available toggle in the bottom right corner, and you should be able to proceed to the next screen:
You can continue to interact with the ShipFast mobile app and everything should just work without any issues, and the final screen should be similar to this one:
Next we will launch a MitM attack to be able to inspect the HTTP traffic between the mobile app and its API backend.
By following the setup instructions for mitmproxy you should already have a terminal open with the mitmproxy CLI:
The proxy is listening on port 8080 for the IP address of your WiFi network.
emulator -avd pixel-android-api-29 -writable-system -http-proxy http://192.168.0.08:8080 &> /dev/null &
After the device have completed to boot you can proceed to the next step.
First, we need to stop the current running instance:
adb shell am force-stop com.criticalblue.shipfast.api_key
Now, launch the ShipFast app again via adb with:
adb shell am start -n com.criticalblue.shipfast.api_key/com.criticalblue.shipfast.LoginActivity
Now, login with Auth0, and when you have the main screen click the toggle button in the right bottom corner, and you should be able to keep interacting with the mobile app without seeing any error message. However, if you look at the mitmproxy CLI you can see that the requests being made to the API backend are now visible:
So we can see the full sequence of requests made during the user authentication flow with Auth0 and the requests made to the ShipFast API backend.
Let’s select the request made to /v1/shipments/nearest_shipment and see its details:
Looking at the headers section of the request we can see that the `Authorization` and `Api-Key` headers are up for grabs so that they can be reused outside the mobile app, for example in automated scripts. Such scripts will now be able to impersonate the mobile app traffic because as far as the API backend will be concerned these scripted requests will look identical to legitimate requests.
Let’s also take a look into the response:
This information is very valuable for an attacker trying to automate attacks against the API backend, because now he has a full view of the life cycle of each request. For example, this approach was what allowed the ShipRaider hacker to build his web app around the ShipFast API backend:
Read more about the ShipFast and ShipRaider story on this blog post.
By understanding how the ShipFast mobile app communicates with its backend the ShipRaider hacker was able to build a web app that allows ShipFast drivers to select the shipment with the best gratuity. This is something that they can’t do in the mobile app because it cuts across the ShipFast company business model. Bypassing the genuine mobile app hurts the company reputation for its very fast and efficient delivery service. That reputation is based on drivers always being directed to the nearest parcel to be delivered, regardless of the gratuity offered, and this concept is broken by ShipRaider - leading to longer delivery times and lower service levels.
Performing a MitM attack is not too hard, just a little laborious, and it allows an attacker to understand in detail how a mobile app communicates with its API, and then use that same knowledge to automate attacks or build other services around it.
Another takeaway is that the API backend cannot trust that a request comes indeed from what it expects, a genuine and unmodified version of the mobile app downloaded from the official stores of Android and iOS, even when the request uses a combination of an API key and user authentication to authenticate themselves to the API.
Read this blog post to learn how ShipFast blocked the ShipRaider web app from accessing the ShipFast API, which now is locked down to only accept requests made from genuine and unmodified instances of the ShipFast mobile app.
See you in my next article How to Bypass Certificate Pinning with Frida on an Android App.