Viewing encrypted SSL/TLS traffic from mobile devices has become more difficult to do with the introduction of certificate pinning, sometimes also call SSL pinning. Normally there is a set of trusted certificate authorities (CAs) on a system that is used to validate a certificate, so as long as the certificate was signed by a trusted CA then it is trusted. This was easy to workaround as you could just add a self signed certificate to your trusted certs and use that self signed certificate to decrypt the traffic being sent and received. Certificate pinning adds an extra layer of security to this by bundling the expected certificate public key with the app and verifying the certificate on the endpoint being connected to matches this.
While attempting to view traffic being sent by the Uber app on Android with Burp Suite, I couldn’t get it to pass any traffic and came to the realization that it was using certificate pinning.
Here are some very rough notes describing how to workaround the certificate pinning that is implemented in Uber’s Android app. The general idea is that we’ll decompile the APK, find the password for certificate keystore, add the self signed Burp certificate into the password protected keystore for the app, and finally recompile a new version of the app. This guide assumes you are using Burp Suite Community Edition as the tool to sniff traffic.
- Make a working directory and move to it
mkdir ~/uber && cd ~/uber
- Download Burp Suite Community Edition and under Proxy tab->Options tab click the ‘Import / export CA certificate’ and export the certificate in DER format to
- Convert the Burp certificate to PEM format
openssl x509 -inform der -in burp.cer -out burp.pem
- Download Bouncy Castle jar (e.g. bcprov-jdk15on-160.jar) to
bc.jar. This is used to add the Burp certificate into the Uber app keystore.
- Download zipalign to
zipalignand make it executable (chmod +x zipalign). This is used to align the APK file after rebuilding it with the new certificate.
- Download apktool to
apktool.jar. This is used to decompile and recompile the APK.
- Download jd-gui. This is used to decompile APK into Java source files. This makes it possible to search for the keystore password.
- Download a copy of the Uber APK file to
- Follow these instructions to install
burp.pemas a certificate on your Android device
Decompiling Uber App
First you’ll need to decompile the Uber app with
java -jar apktool.jar decode uber.apk
Modify Network Security configuration
The first step we’ll take is to modify the network security settings for the app to allow user provided system certificates. For apps that don’t implement certificate pinning, this may be all that is required to be able to start sniffing TLS traffic.
Add the file ~/uber/uber/res/xml/network_security_config.xml with this content:
<network-security-config> <base-config> <trust-anchors> <!-- Trust preinstalled CAs --> <certificates src="system" /> <!-- Additionally trust user added CAs --> <certificates src="user" /> </trust-anchors> </base-config> </network-security-config>
tag in ~/uber/uber/AndroidManifest.xml to include a new attribute
<application android:allowBackup="true" android:networkSecurityConfig="@xml/network_security_config" ...>
Finding Password for Keystore
Since certificate pinning is in use here, we also need to find the place where the expected certificates are being stored. In this case, a password protected keystore named
ssl_pinning_certs_bk146.bks is where this can be found. Of course the plaintext version of the password has to live somewhere in the app or else it wouldn’t be able to access the keystore either. You’ll need to find this password in order to unlock the keystore and add in a custom certificate. To find this password, I used jd-gui to decompile APK back into Java files and searched for ssl_pinning_certs_bk146 in the code. For example, in v4.214.10002 of the Uber app, the password is
sMdqVqJBdBmmkDMp6BK7EVeEkHcNbJ - this may of course change in future version of the app.
Adding Certificate into Keystore
keytool to import burp certificate into the Uber keystore, enter the discovered password to unlock, and then say yes to add it as a trusted certificate.
cd ~/uber/uber/res/raw keytool -import -v -trustcacerts -alias workaround -file ~/uber/burp.pem -keystore ssl_pinning_certs_bk146.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath ~/uber/bc.jar Enter keystore password: <enter this password from above and hit enter> Owner: CN=PortSwigger CA, OU=PortSwigger CA, O=PortSwigger, L=PortSwigger, ST=PortSwigger, C=PortSwigger Issuer: CN=PortSwigger CA, OU=PortSwigger CA, O=PortSwigger, L=PortSwigger, ST=PortSwigger, C=PortSwigger Serial number: 53af3569 Valid from: Sat Jun 28 14:36:41 PDT 2014 until: Sun Jun 28 14:36:41 PDT 2037 Certificate fingerprints: MD5: EF:F9:D6:60:38:A5:29:C2:F5:2D:28:0F:52:88:AE:49 SHA1: 69:1A:25:34:CE:CF:DC:CC:19:76:9E:99:97:14:30:E4:97:77:3E:AB SHA256: E7:0C:93:EF:8F:5D:14:92:78:73:DC:78:94:67:3D:E9:10:61:A1:9D:E7:7B:A3:CF:AA:73:BA:7A:40:6E:C4:16 Signature algorithm name: SHA256withRSA Subject Public Key Algorithm: 2048-bit RSA key Version: 3 Extensions: #1: ObjectId: 18.104.22.168 Criticality=true BasicConstraints:[ CA:true PathLen:0 ] #2: ObjectId: 22.214.171.124 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 49 12 56 F4 D7 B1 EB BD BE AC BA 67 0B 23 D9 3B I.V........g.#.; 0010: 57 8E 12 B5 W... ] ] Trust this certificate? [no]: yes Certificate was added to keystore [Storing ssl_pinning_certs_bk146.bks]
Create Keystore for Signing New APK
keytool to create a signing key that will be used to sign the modified APK. This prompts you to enter a password for the keystore and key (this can be any password), and to provide the Distinguished Name fields for your key. It then generates the keystore as a file called my-release-key.jks, saving it in the current directory. The keystore contains a single key that is valid for 10,000 days.
cd ~/uber keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias Enter keystore password: Re-enter new password: What is your first and last name? [Unknown]: What is the name of your organizational unit? [Unknown]: What is the name of your organization? [Unknown]: What is the name of your City or Locality? [Unknown]: What is the name of your State or Province? [Unknown]: What is the two-letter country code for this unit? [Unknown]: Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct? [no]: yes Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 10,000 days for: CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown Enter key password for <my-alias> (RETURN if same as keystore password): [Storing my-release-key.jks] Warning: The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore my-release-key.jks -destkeystore my-release-key.jks -deststoretype pkcs12".
Building Modified APK
Move back to main directory, use
apktool to build,
jarsigner to sign the APK and
zipalign to align it.
cd ~/uber/uber java -jar ../apktool.jar build . -o ../modified_uber.apk cd ~/uber jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.jks modified_uber.apk my-alias jarsigner -verify -verbose -certs modified_uber.apk ./zipalign -v 4 modified_uber.apk modified_uber-aligned.apk