Let’s write Swift code to intercept SSL Pinning HTTPS Requests

One of the popular ways to inspect HTTPS requests in iOS Apps is the Man-in-the-middle (MITM) attack. This technique requires a machine to be used as a proxy server for the client. For this to work, the client has to install the proxy server’s certificate into the device global trust store. By doing this, the client whitelists the certificate and hence allows HTTPS communication with the proxy server.

Here’s an example of mitmproxy being used to inspect CNN iOS app. Prior to this, I have already installed the proxy server’s certificate on the device. Instructions can be found on mitmproxy website here

Using mitmproxy to inspect CNN iOS App’s requests

SSL Certificate Pinning

One way to protect your app against MITM is to use SSL certificate pinning. This involves having a copy of the trusted server’s certificate packaged into your iOS app and some additional code to ensure that the app only communicates with servers using that particular certificate. When SSL certificate pinning is active, the app will not allow any requests to be sent out to any untrusted server. Hence, MITM proxy servers will not be able to pick up the requests because the requests are not sent over the secured network channel.

Twitter iOS app employs SSL certificate pinning as a security feature for requests sent to https://api.twitter.com. MITM tools such as mitmproxy and Charles Proxy wont be able to inspect any Twitter api requests.

A solution to get around SSL certificate pinning is to attempt to intercept the requests BEFORE it gets sent over the secured HTTPS channel. This technique requires us to plant codes into the app so that we have direct access to the URLRequest object we want to inspect. So let’s first visit Apple’s URL Loading System.

URLProtocol — Apple’s URL Loading System

In Apple’s URL loading system, different URLProtocols are defined to handle different types of URL. Here’s the steps to create our own custom URLProtocol subclass.

1. Each URLProtocol subcalss class should minimally define the following

2. Register the new URLProtocol subclass with the loading system

3. Adding custom logging functions in canInit(with:) function.

We will be logging a string representation of the URLRequest instance in cURL command format to the console and to a slack channel (see line 28).

Putting all these together

Here’s the plan to allow us to inspect all network requests.

  1. Create a new Xcode Dynamic Framework Project. Here’s the github reference for this. https://github.com/depoon/NetworkInterceptor
    Take note! This post is based on tag `0.0.1`.
  2. Create our own custom URLProtocol. eg CustormUrlProtocol. Add in custom code to create cURL command strings. In addition we will print out the cURL strings to the device console as well as sending a copy to a slack channel (just for the fun of it). https://github.com/depoon/NetworkInterceptor/blob/master/NetworkInterceptor/Source/RequestInterceptor/CustomUrlProtocolRequestInterceptor.swift#L51-L155
  3. We will register the new URLProtocol subclass into the loading system and perform method swizzling to ensure our protocol class gets evaluated before any existing apple protocol classes. https://github.com/depoon/NetworkInterceptor/blob/master/NetworkInterceptor/Source/RequestInterceptor/CustomUrlProtocolRequestInterceptor.swift#L11-L49
  4. I have created a Slack channel request logger for this project. https://github.com/depoon/NetworkInterceptor/blob/master/NetworkInterceptor/Source/RequestLogger/SlackRequestLogger.swift
  5. Build the framework
  6. You can drag and drop the framework into your other Xcode projects and start using this feature. Do check the console and your slack channel for the cURL commands.
  7. For iOS Device .ipa files, you will need to inject the framework (in iphoneos architecture) into the ipa file.

Code Injection

The Dynamic Library framework code injection technique can be found in my previous medium post. Great thing about this is that you do not need a jail-broken device for this to work.

Do take note that you wont be able to perform code injection on production appStore iOS Apps as they are digital rights management (DRM) protected. Fortunately, you can find cracked versions of appstore apps on https://www.iphonecake.com. However, proceed at your own risk, you do not know whether these cracked apps have been previously altered with malicious codes.

Tinder’s API Request

Here are the requests I managed to see when I load the dynamic framework onto the Tinder iOS app.

Oops, I see Tinder’s api
cURL commands of Tinder requests sent to a Slack Channel

Hmmm, I guess I did not like someone’s Tinder profile. Api request to “Pass” up on someone.

Facebook api requests are also seen

You will also notice that Tinder is also sending requests to Facebook Api. That is because Tinder has FacebookSDK embedded into the app and uses features like login.

Sadly, SSL Certificate pinning was not enabled on Tinder app. Anyone can trivially use a MITM tool to inspect all the requests. So let’s find another app.

Twitter’s API Request

As mentioned earlier, MITM tools will not be able to see any api.twitter.com requests on the console due to SSL certificate pinning. Attempts to login on the Twitter will fail as the app throws an authentication error and does not allow the request to be sent over the network.

Twitter’s login api

After injecting our framework and disabling our proxy, we are able to see the login api request “/auth/1/xauth_password.json” including our ID and password values in the parameters (look at the 2nd last line shown in the Slack channel message).

Do note that upon successfully login into Twitter, the app crashed as the app detected that I am using my own development provisioning profile to sign the app and the app’s signature conflicted with KeyChain security. This is a trivial issue to resolve but for this medium post, I do not wish to further share app specific exploits outside of our main topic.

Tech Talk 2018

Here’s the iOS Dev Scout Meetup talk I gave on 30th May 2018 in Singapore. Towards the end, I showcased how I could inspect Twitter app’s HTTPS requests even when SSL pinning is active.

GitHub Repository References

Ending

I hope this article has given you insights. Feel free to try out the codes and inspect requests in your favourite iOS app. Do drop me an email de_poon@hotmail.com when you manage to succeed or require additional help to get it working.

@de_poonKenneth