Sniffing SSL traffic on Android (Vipps Payment App)

I previously attempted to do the same with an early version of the Vipps (Norwegian easy payment app), but the app has changed a lot since then. I decided to try again with the current version, and I learned that it has implemented quite a few new countermeasures preventing the old method from working.

The process

First download Vipps app on an Android phone, then connect it and copy the app over USB:

adb shell pm list packages | grep vipps
adb shell pm path no.dnb.vipps

adb pull /data/app/no.dnb.vipps-QLoJ4giHA8cXj7Pl6RI20A==/base.apk
adb pull /data/app/no.dnb.vipps-QLoJ4giHA8cXj7Pl6RI20A==/split_config.arm64_v8a.apk
adb pull /data/app/no.dnb.vipps-QLoJ4giHA8cXj7Pl6RI20A==/split_config.xxhdpi.apk

Note that app is split into 3 apk’s. Now decompile the main APK:

apktool -r d base.apk

This will extract APK contents to base. I then used jaxd-gui to decompile and inspect the (obfuscated) Java/smali source code.

Now we need to remove the code that checks the SSL certificates inside the X509TrustManagers. These can be found in base/smali_classes3/util/h/bs/a.smali and base/smali_classes3/util/ma/c.smali. Just return void early:

.method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
+ return-void

Next, inside the file base/smali/com/dnb/vipps/android/common/networking/h.smali there is extra certificate pinning code, we need to remove that:

    public final l.h a() {
h.a aVar = new h.a();
- aVar.a("", "sha256/nTNUIFsFUeN3c+hYZzlJfKRIwoRtRDyY6sVGPEauJWY=");
- aVar.a("", "sha256/O+qzlJSyoBSdYsSMceGYjjRqDxKVzUFjX0ymP+AC0f0=");
- aVar.a("*", "sha256/nTNUIFsFUeN3c+hYZzlJfKRIwoRtRDyY6sVGPEauJWY=");
- aVar.a("*", "sha256/O+qzlJSyoBSdYsSMceGYjjRqDxKVzUFjX0ymP+AC0f0=");
- aVar.a("", "sha256/Fig44L8aGm+MmceSD17e15lV7pWlsDvgViYUSaj/mqM=");
- aVar.a("*", "sha256/Fig44L8aGm+MmceSD17e15lV7pWlsDvgViYUSaj/mqM=");
return aVar.b();

Next, the Android operating system provides an additional network security config. Add to application in base/AndroidManifest.xml:

<application ... android:networkSecurityConfig="@xml/network_security_config">

Then create a new file base/res/xml/network_security_config.xml with the contents:

<?xml version="1.0" encoding="utf-8"?>
<base-config cleartextTrafficPermitted="true">
<certificates src="user" />

Now recompile the modified APK:

apktool b base -o vipps-modified.apk

Now create a debug signing certificate for signing the APKs with:

echo y | keytool -genkey -v -keystore debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "cn=Mark Jones, ou=JavaSoft, o=Sun, c=US"

Re-sign all APKs using the same debug keystore just created:

echo android | apksigner sign --ks debug.keystore --out vipps-signed.apk vipps-modified.apk
echo android | apksigner sign --ks debug.keystore --out split_config.arm64_v8a-signed.apk split_config.arm64_v8a.apk
echo android | apksigner sign --ks debug.keystore --out split_config.xxhdpi-signed.apk split_config.xxhdpi.apk

Install all the new APKs to the Android phone

adb install-multiple vipps-signed.apk split_config.arm64_v8a-signed.apk split_config.xxhdpi-signed.apk

Run Charles proxy with SSL proxying and add the following domains to includes:

From Charles, export the generated root certificate and download it to your Android phone and trust it for all apps/browser.

On the Android phone, launch

Start the app and start sniffing SSL traffic!

Next steps / observations

All Vipps related requests from startup, OTP, BankID auth and setting PIN as well as logging in:

Other reverse engineering tips

  • Dump resources (strings/translations): aapt dump resources base.apk | less