Uncertify: A tool for recompiling Android apps bypassing different certificate pinning techniques
In these times of social isolation and worldwide quarantines not all that is coming at us is bad. We all have more time now for some reading, programming, learning…let’s say introspection in a general way. I’ve spent some of this new allotted free time hacking with the concept of bypassing certificate pinning in Android apps.
Certificate pinning is a common security technique that is used to restrict the set of X.509 certificates an application supports (for a good introduction about certificate pinning I recommend this article). Android developers have different methods available to enable certificate pinning in their Android apps. Let’s describe some of them and how Uncertify bypass them.
Network Security Configuration
The network security configuration feature allows an app to customize its security configuration in a declarative way using an XML file. This is very convenient because no code is necessary. The next snippet could be an example of how to enable certificate pinning for domain example.com
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<trust-anchors>
<certificates src="@raw/my_certificate"/>
</trust-anchors>
</domain-config>
</network-security-config>
Note that my_certificate must be a certificate in DER or PEM format stored in the res/raw
folder and the xml file must be created asres/xml/network_security_config.xml
A reference to this file must be also added to in the Android Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
Uncertify changes the network configuration file to a very permissive one.
<?xml version="1.0" encoding="utf-8"?><network-security-config> <base-config> <trust-anchors> <certificates src="user"/> <certificates src="system"/> </trust-anchors> </base-config></network-security-config>
This network configuration file allows the app to trust any certificate validated by the device trusted CAs and any self-signed certificate or CA installed by the user. This is done by Uncertify regardless of the target app is using this feature or not.
OkHttp certificate pinning
OkHttp is a Http client developed by Square that is probably the most used http client library in the Android development community. Okhttp has very easy to use apis for enabling certificate pinning. Here it is an example of how to use OkHttp with certificate pinning:
String hostname = "yourdomain.com";CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build(); OkHttpClient client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build(); client.newCall(request).execute();
Uncertify modifies the internals of OkHttp library to ignore any CertificatePinner object passed to the OkHttpClientBuilder
The old school: TrustManagers
TrustManager is the component responsible for deciding if an app should trust or not the credentials. Here it is a common example:
Code obtained from: https://www.netguru.com/codestories/3-ways- how-to-implement-certificate-pinning-on-androidval resourceStream = resources.openRawResource(R.raw.my_certificate)
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType)
keyStore.load(resourceStream, null)val trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm)
trustManagerFactory.init(keyStore)val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustManagerFactory.trustManagers, null)
val url = URL("http://www.example.com/")
val urlConnection = url.openConnection() as HttpsURLConnection
urlConnection.sslSocketFactory = sslContext.socketFactory
Uncertify modifies:
sslContext.init(null, trustManagerFactory.trustManagers, null)
to:
sslContext.init(null, null, null)
Thus allowing to bypass all trustmanagers passed to the SSLContext. It is worth mentioning that Uncertify not only modifes those calls made by the proper app but also those made by the library.
How to use Uncertify
There is more information in the README.md file but here it is the basic commands needed to get it done.
git clone https://github.com/felHR85/Uncertify.git
cd Uncertify
python3 uncertify.py PATH_TO_APK --pinning
Uncertify depends on
Apktool
Jarsigner
Zipalign
Currently it has been tested on Linux and it should also run in OS X. Next steps should be:
Windows support
Docker
If you find some bugs or you have suggestions please feel free to create an issue in Github.
Happy coding!