Pin There, Done That!

Understanding and Defeating Certificate Pinning in Android Applications

Abhishek Agarwal
11 min readJan 10, 2022

In this post, we will be looking at how certificate pinning is implemented in Android applications and these can be defeated. We will follow this up with some recommendations on pinning. So sit tight and let’s begin!

What is pinning?

Let say a mobile application has to communicate with a TLS supported endpoint. During the SSL handshake, it will verify the server’s certificate via the ‘chain of trust’. Certificate pinning restricts the set of allowed server’s certificates and the application checks if the presented certificate belongs to that restricted set.

Through certificate pinning (or SSL pinning), the backend server is associated with a particular X.509 certificate or public key. Instead of accepting any certificate signed by a trusted certificate authority, the application is designed to accept only certain certificates.

After storing (“pinning”) the server certificate or public key, the mobile application subsequently connects to the known server only. The app will not trust custom certificates and will not allow proxy tools to intercept the traffic.

Why use pinning?

The rationale goes that withdrawing trust from external certificate authorities reduces the attack surface and gives an additional layer of security.

Pinning effectively removes the “conference of trust”. An application which pins a certificate or public key no longer needs to depend on others — such as DNS or CAs — when making security decisions relating to a peer’s identity. — OWASP

Pinning is done when one wants to be relatively certain of the remote host’s identity or when operating in a hostile environment.

For this reason, many mobile applications especially those belonging to banking and the financial sector, implement some sort of pinning. As mentioned earlier, this makes traffic interception and MITM attacks relatively difficult. So pen testers working with mobile applications need to defeat this pinning before they can see all the juicy traffic between an application and the backend server.

Approaches to Pinning

There are two basic approaches to pinning — Certificate Pinning and Public Key Pinning.

Certificate Pinning

In certificate pinning , the developer hardcodes the SSL certificate (X.509 certificates) into application code. When the application communicates with the server, it checks whether the same certificate is present or not.

If it is present, the application proceeds with the communication. Otherwise, it will throw an error. The downside to this type of pinning is if the certificates are rotated on a regular basis, then the application needs to be updated regularly too.

Public Key Pinning

In this, the underlying public keys are pinned and used to check the integrity of the connection. Public key pinning is more flexible but a little trickier due to the extra steps necessary to extract the public key from a certificate.

As with a certificate, the program checks the extracted public key with its embedded copy of the public key. The underlying public keys in certificates can remain static. Hence it is immune to the certificate rotation problem.

The hash of the SubjectPublicKeyInfo is pinned.

Sidebar — X.509 Certificates

Structure of X.509 Certificates

X.509 is a standard, defining the format of public key certificates. Subject Public key Info (SPKI) contains the public key of the server and is used by client to encrypt data during HTTPS communication.

Burp Suite’s CA Certificate

This signature of the certificate is computed by the issuer. The algorithm — sha256withRSA implies that the certificate is hashed via SHA-256 and then encrypted with issuer’s private (RSA) key.

Using the public key of the issuer, the encrypted hash is decrypted and compared with the computed hash. If they match, it means the certificate is valid. More details on manual verification can be found here.

How Pinning is implemented?

An android application has multitude of ways to implement pinning. Let’s look at how each of them is implemented.

Using Network Security Configuration

Since Android N (API Level 24), the preferred way for implementing pinning is by leveraging Android’s Network Security Configuration feature. It is extremely easy to implement and allows the customization of network security settings in a safe, declarative configuration file without modifying app code.

Implementation

  1. The manifest file points to the network security configuration file via the android:networkSecurityConfig attribute on the application tag.

2. The network security configuration file is created for pinning.

To bypass:

  • Try removing the attribute-
    android:networkSecurityConfig=”@xml/network_security_config”
    from the manifest file.
  • Try replacing/adding the hashes in network_security_config.xml with your own certificates.

Using OkHttp and CertificatePinner

OkHttp is a very popular HTTP client library for Java and Android.

Implementation

To bypass, replace the hash with your own certificate’s hash.

Using TrustManager

TrustManager is a component responsible for deciding whether the app should accept credentials submitted by the peer or not.

Implementation

  1. Certificate file (example_cert) is added to the app resources in /res/raw.
  2. The KeyStore is loaded with the certificate file from resources (as InputStream)

3. The TrustManagerFactory is fetched and initialized with KeyStore.

4. SSL context is created with TLS protocol and then a secure SSL connection is built with the TrustManager.

To bypass, replace the pinned certificate file (example_cert) with your own certificate file in /res/raw.

Pinning with Retrofit

Retrofit is a popular HTTP client and leverages OkHttp for certificate pinning.

Pinning in Webview

Applications that use a WebView component may utilize the WebViewClient’s event handler for some kind of “certificate pinning” of each request before the target resource is loaded. The following code shows an example verification:

Pinning in Xamarin Applications

Applications developed in Xamarin will typically use ServicePointManager to implement pinning. Normally a function is created to check the certificates and return the boolean value to the method ServerCertificateValidationCallback.
In this particular example we are pinning the intermediate CA of the certificate chain.

Pinning in Cordova Applications

Hybrid applications based on Cordova do not support Certificate Pinning natively, so plugins are used to achieve this. The most common one is PhoneGap SSL Certificate Checker. The check method is used to confirm the fingerprint and callbacks will determine the next steps.

After decompressing the APK file, Cordova/Phonegap files will be located in the /assets/www folder. The plugins folder will give the visibility of the plugins used. Search for these methods in the JavaScript code of the application to confirm its usage.

Bypassing Pinning

When an proxy is unable to intercept requests and responses through a mobile application, it is more than likely that pinning has been implemented. We can bypass it either at runtime (dynamically) or statically.

Dynamic Pinning Bypass

Bypassing the pinning logic dynamically is generally faster and convenient. Most standard implementations can be bypassed this way. The are two main approaches:

Using Automated Tools

Frida
Frida is a dynamic instrumentation toolkit which allows us to hook into methods during runtime and inject code. We can use the Universal Android SSL Pinning Bypass with Frida or Multiple SSL Pinning Bypass scripts.

Objection
Objection is runtime mobile exploration toolkit based on Frida. We can use the following command- android sslpinning disable

Xposed
Use Xposed framework to install and use any of the following modules:

Method Hooking

This method relies on finding the correct method to hook. Once we hook into the method, we can modify the return value to bypass pinning. For example changing False to True for boolean return type.

Most implementations rely on reusing existing libraries and hence it is advisable to search for strings that identify such libraries. Once you identify the library, examine the source code and find methods which you can hook into.

After identification of methods, we need to hook the suspected methods with Frida and print the arguments. They may print out a domain name and/or a certificate hash, after which the we can modify the arguments to circumvent the implemented pinning.

As an example, let’s say that an application uses an obfuscated OkHTTP3 library. The CertificatePinner.Builder class is responsible for adding pins for specific domains. If you can modify the arguments to the Builder.add method, you can change the original hashes to the hashes belonging to your certificate.

The correct methods to hook can be found by —

  • Searching for hashes and domain names. The actual pinning method will typically be used or defined in close proximity to these strings.
  • Searching for keywords related to implementation methods like: Builder.add, SSLContext.getInstance(“TLS”), fingerprint etc.
  • Search for the method signature in the SMALI code.
    Example: For the Builder.add method, you can find the possible methods by running the following grep command:
grep -ri java/lang/String;\[Ljava/lang/String;)L ./
  • This command will search for all methods that take a string and a variable list of strings as arguments, and return a complex object. Depending on the size of the application, this may have one or multiple matches in the code.

Static Pinning Bypass

Bypassing pinning statically involves replacing the pinned certificate/hashes with your own custom ones. Somewhere in the application, both the endpoint and the certificate (or its hash) must be defined. After decompiling the application, we can search for:

Certificate hashes:

Find hashes using: grep -ri "sha256\|sha1" ./smali

We can replace the identified hashes with the hashes of our certificates.

Certificate files:

Find certificate files using: find ./assets -type f\(-iname\*.cer -o -iname\*.crt\)

We can replace these files with our certificates.

Truststore files:

Find trust store files using: find ./ -type f\(-iname\*.jks -o -iname\*.bks\)

Once we find the location, we can add our own certificates to the trust store.

After making the required modifications, we can repackage the application using apktool.

Note: If the pinning code is not visible in the android code or we have not been able to bypass it using the above techniques, then it is possible that the pinning has been implemented in native libraries.

This link can be helpful in understanding how to bypass pinning in such situations.

Conclusion

Certificate Pinning is a strong strategy for ensuring secure communication. However, it is interesting to note that that the official Android docs do not recommended it:

due to the high risk of future server configuration changes, such as changing to another Certificate Authority, rendering the application unable to connect to the server without receiving a client software update. — Android Docs

Be that as it may, many applications still implement it.

It is also important to note that without effective jailbreak/root detection, strong obfuscation, anti-tamper detection and other advanced binary/runtime protections, pinning implementations remain weak. When an application with certificate pinning is allowed to run on a rooted device, an attacker can use easily dynamic instrumentation strategies to defeat and bypass the pinning.

Recommendations (For Developers)

What to do when Pinning Fails?

Pinning can fail due to a number of reasons. The certificate could have expired, there might be some network connectivity issue, an attacker might be trying to intercept the traffic via a MITM attack, etc. In the event that certificate pinning fails, the app can:

  • Inform the user about not being able to connect to the backend server and stop all operations. The app can check whether there is an update and inform the user about updating to the latest version.
    The app can disallow any form of interaction until it is updated or the certificate is validated again. This is called Fail hard.
    It involves basically stopping all communication and associated functionality until a patch is available. It is the most secure and easy implementation, but might come at the cost of usability.
  • Do a call to a crash-reporting service including information about the failed pin. The responsible developers should get notified about a potential security misconfiguration.
  • After calling the backend or crash-reporting service to notify about the failed pinning, the app can still offer limited functionality which doesn’t involve sensitive functions or processing of sensitive data. The communication would happen without SSL Pinning and would just validate the X.509 certificate normally. This is called Fail Soft strategy.

Which option is chosen depends on how important the availability is as compared to the complexity of maintaining the application.

Understanding Pin Failures

Large amount of pin failure reports indicate that there is probably a misconfiguration. There is a large chance that the key materials used at the terminating endpoint is different than what the app is expecting. In that case, an update of either that key material or an update of the app should be pushed through.

Very few pin failure reports are an indication that the network is healthy and so is the configuration of the terminating endpoint. Instead, it might well be that there is a man-in-the-middle attack ongoing at the app instance, due to which the pin is failing.

What, Where and When to Pin?

As mentioned earlier, developers can either pin the certificate itself or the public key. According to the OWASP recommendation, there is a downside to pinning certificates. If the site rotates its certificate on a regular basis, then your application would need to be updated regularly. For example, Google rotates its certificates, so you will need to update your application about once a month (if it depends and uses on Google services).

Which certificate to pin?

The choice of certificate impacts the level of security achieved, decreasing as we reach the root certificate. So it is important for the developers to choose carefully.

Leaf certificate: This is the most secure option. If you pin the leaf certificate, then you can be sure if the presented certificate is yours. But as discussed earlier, this presents a problem anytime you want to rotate your certificate. When the certificate is changed, everyone application needs to be updated.

Intermediate certificate: When you pin the intermediate certificate, you are placing your trust in that intermediate CA that it will not issue an unauthorized certificate for your server. The advantage of this approach is that you can rotate your leaf certificate as often as you want, as long as you use the same intermediate CA.

Root certificate: If you pin the root certificate, you are trusting a collection of certificate authorities — the root CA itself as well as any intermediaries it trusts not to issue unauthorized certificates. This is the least secure option as the trust surface and hence the attack surface is larger.

It is generally recommended to pin multiple certificates to decrease the chances of bricking your application. Pinning intermediate and the leaf level certificates generally is a popular approach.

When to Pin?
Preloading
is the preferred way to do pinning. It involves hard-coding the certificates/public key in the app at the time of development. Preloading out of band usually means the attacker cannot taint the pin.

--

--