Analyse an Android App's API Communications

I was curious on how Uber and other similar apps implemented their APIs, and what kind of data are being sent from my phone to these APIs, so I looked into it.

This article is a little summary of my journey into reverse engineering the Uber Eats app to expose its communications.

Tools used

  • Charles Proxy (http://www.charlesproxy.com/) to analyse network packets
  • openssl to create my own Certificate Authority
  • apktools to open apk files
  • jarsigner to sign the app after rebuild
  • adb to communicate with my phone

My phone is running Android Nougat and it is rooted.


Computer-side preparation

Creating a CA

We need to create our own certificate authority and give it to Charles Proxy to let it generate certificates when needed.

Generate the CA

mkdir certificates && cd certificates
openssl req -x509 -days 730 -nodes -newkey rsa:2048 -outform der -keyout server.key -out ca.der -extensions v3_ca

Convert PrivateKey

openssl rsa -in server.key -inform pem -out server.key.der -outform der
openssl pkcs8 -topk8 -in server.key.der -inform der -out server.key.pkcs8.der -outform der -nocrypt

Commands borrowed from the good article from Awakened.

Side note: If you are on MacOS, you’ll probably have an issue with openssl req [...], you must append the following lines at the end of /etc/ssl/openssl.cnf:

[ v3_ca ]
basicConstraints = critical,CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always

Putting the CA as a trusted CA in Android

For the apps to trust the CA, we either need to put it as a trusted CA (rooted phone), or change the app config to trust user certificates (in the config xml when depackaging the app) and follow this tutorial.

We will go through the process of adding the certificate as a trusted one.

Convert PublicKey (change a58355c2.0 with the result of line 2)
openssl x509 -inform der -in ca.der -out ca.pem
openssl x509 -inform PEM -subject_hash_old -in ca.pem | head -1
cp ca.pem a58355c2.0
openssl x509 -inform PEM -text -in ca.pem -out /dev/null>> a58355c2.0
Copy CA to Android
adb push a58355c2.0 /data/local/tmp
adb shell
In the ADB Shell
su
mount -o rw,remount /system
mv /data/local/tmp/a58355c2.0 /system/etc/security/cacerts/
chown root:root /system/etc/security/cacerts/a58355c2.0
chmod 644 /system/etc/security/cacerts/a58355c2.0
reboot

In Charles Proxy

Just import the private key in Charles Proxy as follow:

Create the p12 file
openssl pkcs12 -export -out MyCert.p12 -inkey server.key -in ca.pem

And navigate to Proxy > SSL Proxying Settings > Root Certificate, then select the p12 file and we are good to go.


Listening network packets from an app

On the phone, we need to set our computer as the proxy in the settings.

Every packet coming to or from our phone is now intercepted by Charles Proxy. Because we added our own CA as a trusted CA on the phone, we should be able to see all the traffic in clear, even on HTTPS pages in the browser.

However, this is not true for many apps, in particular for Uber Eats. These apps implement a technology called SSL Pinning. In short, the app keeps the signature of the certificate used for the company API (like api.uber.com). This means the authenticity of the certificate is being checked each time the app is opened, and the app will refuse to connect with the API if the signatures of the certificate (active and stored) mismatch.

Our certificate is used where the Uber Eats app expects its own certificate.

To bypass this, we need to open the apk package of the app, and find a way to disable SSL Pinning.

Opening the app APK file
apktool d -f -r ubereats.apk

Then, try finding in the depackaged files a line containing: const-string v4, "sha256/, the cert signing should be right after this.

You just need to put you Charles Root Certificate signature instead of this one, and the Uber Eats App will accept the communication through Charles.

Repackaging APK file

We now need to repackage the app into an apk file:

apktool b --use-aapt2 ubereats/ -o ubereats_unsigned.apk

This apk file is unsigned, and Android will not allow this to be installed.

So, let’s generate a key and use it to sign the apk:

keytool -genkey -v -keystore ubereats-key.keystore -alias ubereatskey -keyalg RSA -keysize 2048 -validity 10000
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ubereats-key.keystore ubereats_unsigned.apk ubereatskey
mv ubereats_unsigned.apk ubereats.apk
Reinstall the APK

Remove the app from your phone first. Because the signature is different, if the app is still present when installing our own version, Android can throw errors because the signatures don’t match.

With ADB:

adb install ubereats.apk

Start listening

Now that everything is in place, you can just start the app and you will see the packets in clear text inside Charles Proxy.

Don’t forget to set your computer as proxy in your phone settings !