Bypassing Android Certificate Pinning on Singapore Power Utilities App

Ferry Djaja
5 min readFeb 28, 2018

--

I would like to share what I did to bypass the certificate pinning on Singapore Power Utilities Android app. Once it is done we can see the clear TLS traffic data with Charles Proxy and you can retrieve the necessary information with Node app.

SP Utilities app

What is Certificate Pinning?

According to Symantec, “ During the handshake that takes place when an SSL/TLS connection is established the client can authenticate the server it is talking to by validating that the server certificate was issued by a Certificate Authority that the client trusts.”

Certificate pinning will prevent man-in-the-middle attacks. Since we are using Charles Proxy, it will not work if we are not bypassing the certificate pinning. This is one of the security best practice to secure the app and the SP Utilities app is using this method to secure the traffic.

There are few methods to bypass the Certificate Pinning. You can read more details here https://blog.netspi.com/four-ways-bypass-android-ssl-verification-certificate-pinning/

In this document, I will use the method to decompile the app, modify the code to remove the certificate pinning checks and repackage the app.

Decompile the App

  1. Install the app on your Android device.
  2. Open your command prompt and type the following commands:
adb shell
pm list package -f singaporepower

3. Note the path to the package.

4. Pull the app to your local folder.

adb pull /data/app/sg.com.singaporepower.spservices-1/base.apk

5. Decompile the app using apktool.

apktool d -f base.apk

6. You will get the output in the base folder as per below screenshot.

base folder generated from the decompiler

7. Modify AndroidManifest.xml. Open this file and add the following lines under Application tag.

android:debuggable=”true” android:networkSecurityConfig=”@xml/network_security_config
AndroidManifest.xml

8. Add network_security_config.xml under ../base/res/xml folder.

<?xml version=”1.0" encoding=”utf-8"?>
<network-security-config>
<debug-overrides>
<trust-anchors>
<! — Trust user added CAs while debuggable only →
<certificates src=”user” />
</trust-anchors>
</debug-overrides>
</network-security-config>
network_security_config.xml

Remove the Code for Certificate Pinning

Using jadx-gui app, decompile the base.apk again and search for string “certificatePinnerBuilder”. I found it on NetworkManager class under package sg.com.singaporepower.spservices.common.network.

Decompile the app using jadx-gui

Let’s remove these line of codes that I highlighted in the above screenshots.

Go back to base folder again and search for those lines in smali code. We found 4 smali files. Let’s check one by one.

smali codes

Let’s find the corresponding codes from line 247 to line 251 on jadx-gui below on the smali codes.

And I found this on the NetworkManager.smali codes! We need to remove it.

.line 247
new-instance v0, Lokhttp3/CertificatePinner$Builder;
invoke-direct {v0}, Lokhttp3/CertificatePinner$Builder;-><init>()V.line 248
.local v0, “certificatePinnerBuilder”:Lokhttp3/CertificatePinner$Builder;
invoke-interface {v3}, Ljava/util/List;->iterator()Ljava/util/Iterator;
move-result-object v5:goto_1
invoke-interface {v5}, Ljava/util/Iterator;->hasNext()Z
move-result v4if-eqz v4, :cond_4invoke-interface {v5}, Ljava/util/Iterator;->next()Ljava/lang/Object;move-result-object v1check-cast v1, Ljava/lang/String;.line 249
.restart local v1 # “host”:Ljava/lang/String;
sget-object v4, Lsg/com/singaporepower/spservices/common/network/NetworkManager;->CERTIFICATE_PINS:Ljava/util/Map;
invoke-interface {v4, v1}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;move-result-object v4check-cast v4, [Ljava/lang/String;invoke-virtual {v0, v1, v4}, Lokhttp3/CertificatePinner$Builder;->add(Ljava/lang/String;[Ljava/lang/String;)Lokhttp3/CertificatePinner$Builder;goto :goto_1.line 251
.end local v1 # “host”:Ljava/lang/String;
:cond_4
invoke-virtual {v0}, Lokhttp3/CertificatePinner$Builder;->build()Lokhttp3/CertificatePinner;
move-result-object v4invoke-virtual {p1, v4}, Lokhttp3/OkHttpClient$Builder;->certificatePinner(Lokhttp3/CertificatePinner;)Lokhttp3/OkHttpClient$Builder;move-result-object p1

Once you have removed the codes, let’s repackage and install the app.

Repackage and Install the App

  1. Repackage the app.
apktool b base

2. Uninstall the original app.

adb uninstall sg.com.singaporepower.spservices

3. Sign the modified app.

java -jar signapk.jar certificate.pem key.pk8 base.apk base-signed.apk

4. And finally install it.

adb install base-signed.apk

Analyze the Traffic with Charles Proxy

Open the modified app from your Android phone and let’s see the traffic.

  1. Login to View Bills and enter your user ID and password.
    Hooray !!. Now you can see clearly what information being sent to https://jarvis-api-production.apps.spdigital.io/v1/login and what is the response back in JSON format.
    Please take note the access_token and account_number parameters.
Logon traffic

2. Click on the History sub menu, and you will see your electricity, water and gas usage history.

Node App

Now let’s create the NodeJS app to simulate the process. I have created two functions:

  1. function SPLogon(callback)
    Logon function to SP Portal.
    Parameter you need to fill in is your USERID and PASSWORD.

2. function SPHistory(callback)
To get the electricity, gas and water usage history.
Parameter you need to fill in is your ACCOUNT_NUMBER and TOKEN that you received from the Logon process.

Running the program:

Output of the Logon function.

Output from the History function.

In JSON format (I have removed the actual data :)):

What’s Next ?

With this you can build an automation report that you can download monthly and integrate with Google Sheets for further processing or you can even build a bot that can check your monthly bill.

--

--