Burp and iOS 9 App Transport Security

When trying to intercept an iOS 9 App’s traffic using Burp (or any other proxy software), you might run into issues caused by App Transport Security, a new security feature introduced in iOS 9.

By default, App Transport Security will cause most Apps on iOS 9 to only allow network connections to servers with a strong SSL configuration:

  • TLS version 1.2
  • Perfect forward secrecy cipher suites
  • RSA 2048+ bits for the certificate chain

By default Burp does not support connections with these settings - TLS 1.2 is disabled by default - so most iOS 9 Apps will not be able to connect to Burp, when trying to proxy the App’s network traffic.

To address that, enable TLS 1.2 and the right cipher suites in Burp’s SSL settings:

Also, Burp’s default CA certificate is only 1024 bits, so you will have to generate a 2048 bits certificate and private key and import it into Burp (Proxy -> Options -> Import / Export CA certificate).

After updating Burp’s configuration and CA key pair, you should then be able to proxy iOS 9 Apps without any problem.

December 01, 2015
ios

SSL Kill Switch v0.8 Released

With the unexpectedly quick release of an iOS 9 jailbreak, users reported a boot loop issue when installing SSL Kill Switch. I think this was caused by underlying changes to the OS, rather than the tweak itself. Saurik subsequently updated Cydia Substrate and apparently made some changes to MobileLoader, the tweak injection mechanism, as SSL Kill Switch stopped working with following error in the logs:

MS:Error: extension does not have filter

As opposed to previous versions of Cydia Substrate, it seems like tweaks now must provide a plist filter or they won’t be loaded into any processes. Hence, I added the following filter for SSL Kill Switch:

Filter = {
  Bundles = (com.apple.UIKit);
};

This fixed the issue, with the side-effect that SSL Kill Switch will no longer be injected into processes that don’t link UIKit (ie. processes with no UI, such as system daemons like itunesd). If you need to use SSL Kill Switch in a daemon, you will need to modify the filter first and then recompile the tweak.

Go to the project’s Releases page to download the new version.

October 29, 2015
ios

TrustKit 1.2.0 for iOS 9 Released

I just released TrustKit 1.2.0, which adds support for iOS 9. As explained in a previous blog post, Apple implemented a behind-the-scene change in iOS 9 that broke TrustKit (and other tools).

Changelog

  • Complete re-write of the hooking strategy to automatically add SSL pinning to the App’s connections. TrustKit now swizzles NSURLSession and NSURLConnection delegates to add pinning validation to the delegate’s authentication handler methods; for developers who want to call into TrustKit manually, this behavior can be disabled using the TSKSwizzleNetworkDelegates setting. This change was made due to the previous hooking strategy (targeting SecureTransport) not working on iOS 9.
  • The pinning policy format has slightly changed, in order to add new global settings: TSKSwizzleNetworkDelegates, TSKIgnorePinningForUserDefinedTrustAnchors, TSKPinnedDomains. If you have an existing pinning policy for TrustKit 1.1.3, all you need to do is put it under the TSKPinnedDomains key.
  • Greatly simplified the TSKPinningValidator API to make it easy to write authentication handlers that enforce the App’s SSL pinning policy. Sample code describing how to do it is available in the documentation.
  • Updated Xcode project settings: stricter warnings, enabled bitcode, separate iOS and OS X build schemes.
  • Pinning failure reports now also send the IDFV in order to simplify the troubleshooting of errors, by being able to detect a single, malfunctioning device.

More information is available on the project’s github page.

Migrating from a previous version

If you were already using TrustKit in your App, here are a few things to take into account when updating to 1.2.0:

  • The new hooking technique based on swizzling NSURLSession and NSURLConnection delegates will only protect connections initiated via these APIs, while the previous implementation would also work on other APIs such as UIWebView and NSStream. The updated Getting Started guide has guidelines on how to leverage TrustKit for these network APIs.
  • As explained in the changelog, the policy format has slightly changed. For your existing policy to work on 1.2.0, you just need to put it under the TSKPinnedDomains key.
  • If you don’t want TrustKit to auto-magically try to enforce SSL pinning on your App’s connections, you can now disable this behavior by setting the TSKSwizzleNetworkDelegates to NO, and instead call into TrustKit manually within your App’s authentication handlers, using the TSKPinningValidator class.

TrustKit and PayPal

Unrelated to the 1.2.0 release but of particular interest is the fact that TrustKit was featured in a post about “Key Pinning in Mobile Applications” on PayPal’s engineering blog!

October 20, 2015
ios

TrustKit, iOS 9 And The Shared Cache

In August, Angela Chow, Eric Castro and myself released TrustKit at the Black Hat conference: an iOS & OS X library designed to make it very easy to deploy SSL pinning within an App.

When Apple released iOS 9 last month, it broke TrustKit; this post explains the behind-the-scene change that caused this and why it affected TrustKit.

TrustKit on iOS 8

As explained in our Black Hat talk, TrustKit worked by patching SecureTransport’s SSLHandshake() function at runtime. During the SSL handshake, this function gets called multiple times until the handshake is completed. Throughout these calls, TrustKit just forwarded them to the real SSLHandshake(), until it returned noErr, which means the handshake was succesful.

When that specific return value was detected and before returning it to the caller, TrustKit performed SSL pinning validation and changed the return value to an error if the validation had failed.

Here is how it looked like in TrustKit:

// Our replacement function for SSLHandshake()
static OSStatus replaced_SSLHandshake(SSLContextRef context)
{
    // First let's forward the call to the real SSLHandshake()
    OSStatus result = original_SSLHandshake(context);
    if (result == noErr)
    {
        // The handshake was sucessful
        // Let's perform SSL pinning validation
        
        // code starts here...
        // <code>
        // ...code ends here
    }
    return result;

Because every Apple Framework (NSURLConnection, NSURLSession, UIWebView, etc.) relies on SecureTransport for SSL, patching SSLHandshake() ensured that all of the App’s outgoing connections were automatically protected.

To do the actual hooking of SSLHandshake(), we used Facebook’s fishhook, which “provides functionality that is similar to using DYLD_INTERPOSE on OS X.”

What Changed in iOS 9

When iOS 9 was released, I received reports that TrustKit wasn’t working anymore, which was surprising to me as I had done some testing myself to ensure it worked fine.

I did some further investigation and discovered something very surprising: TrustKit would work fine on specific devices (such as the iPhone 4S or the iPad 3) but not on other devices (such as the iPhone 6), where TrustKit was successfully getting initialized but no pinning validation would actually be done.

This behavior implied that the hooking of SSLHandshake() was not actually working, although fishhook had been updated for iOS 9. After quite a lot of testing and googling, I luckily found the answer within a tweet from @comex from a few months back, talking about the beta of iOS 9:

To understand what this means, we first have look at how fishhook works. At a high level, fishhook modifies at runtime the process’ symbol table to have the symbol you want to hook point to your own code, instead of the original C function. Then, when any code wants to call that function, it looks up its symbol within the symbol table, which then points to your replacement function. Some more technical details about this symbol rebinding technique are available on the project’s page.

The iOS 9 change @comex described means that any code within the dyld shared cache will actually skip the symbol table and jump straight to the function itself, when calling another function in the cache. This was probably done as a performance optimization to skip the symbol lookup step.

Consequently, modifying the symbol table doesn’t affect calls happening within the shared cache (such as a method within NSURLConnection calling SecureTransport’s SSLHandshake(), as both frameworks live in the shared cache).

This explains why TrustKit stopped working on iOS 9:

  • Calls happening across Apple frameworks and libraries can no longer be hooked at all, meaning that TrustKit’s SSLHandshake() hook would only be triggered if the App itself directly calls this function.
  • This optimization can be enabled by Apple when compiling the shared cache, which is why TrustKit stopped working only on specific devices. It seems like the iOS 9 image for the iPhone 4S has the optimization disabled, while the one for the iPhone 6 has it enabled.

Unfortunately, this also means that some of the techniques we presented at the Black Hat talk for writing secure libraries through function hooking do not work on iOS 9. This also affects other Apps and products as I have been in contact with developers who faced the exact same issue.

What about Cydia Substrate and tweaks?

If you’re developing tweaks for jailbroken iOS and hooking C functions using Substrate, this change will not affect your code.

The reason for this is that, as explained in our talk, Substrate takes a different approach for hooking C functions: it actually modifies the instructions at the beginning of the function you want to hook, in order to insert some trampoline code to jump to your replacement function. Of course, this technique can only work on jailbroken devices (as it requires RWX memory pages). Therefore, Substrate’s hooking implementation will work regardless of who is calling the function (an Apple framework or the actual App).

What’s next for TrustKit ?

I will release TrustKit 1.2.0 for iOS 9 in the next few days, which has a completely different implementation for “hooking” into the App’s SSL connections to add pinning validation. I had to take the more conventional (but not as neat) approach of swizzling the App’s NSURLConnection and NSURLSession delegates.

In the release blog post which will come soon, I will provide more details about what has changed in TrustKit 1.2.0 and how to deploy it in your App; this version also brings some significant improvements over 1.1.3.

October 17, 2015
ios

Multipeer Connectivity on iOS 9

Apple just released iOS 9, which fixes a good number of security issues, and includes a mitigation for a man-in-the-middle attack on Multipeer Connectivity I presented last year at Black Hat US:

The issue was that an attacker on the network could downgrade the encryption level of a Multipeer Connectivity session configured with MCEncryptionOptional to MCEncryptionNone (ie. plaintext communication), even if authentication was enabled.

Apple did not fix the core issue with MCEncryptionOptional, as it would require significant changes to the implementation, in order to have each peer validate the security settings exchanged during the handshake after authentication is performed.

However they changed the default encryption level (used when the App does not explicitly specify one) from MCEncryptionOptional to MCEncryptionRequired, which is not vulnerable to the downgrade attack. Overall, this change should protect most Apps, but the ideal solution would have been to remove MCEncryptionOptional altogether.

September 19, 2015
ios

Apple Security Framework Wish List

While working on TrustKit, I ran into significant challenges when trying to implement SSL pinning the right way. The best practice is to pin the certificate’s Subject Public Key Info (ie. the public key and the key’s algorithm/type). However, the Security framework on iOS and OS X does not provide comprehensive APIs for parsing certificates or interacting with the OS’ trust store.

While I did manage to implement SPKI pinning in TrustKit, the workarounds I had to use are unsatisfying, and I hope that Apple can eventually extend the Security framework to make it easier to implement SSL pinning code.

Extracting the public key data from a certificate

On iOS, the only way to retrieve the public key bits from a certificate is convoluted:

  1. Create a SecTrustRef trust object with the certificate, and call SecTrustEvaluate() on it.
  2. Call SecTrustCopyPublicKey() on the trust object to retrieve a SecKeyRef of the public key.
  3. Add the public key to the device’s Keychain using SecItemAdd().
  4. Retrieve the public key from the Keychain and set kSecReturnData to YES in order to specify that the key’s data should be returned (instead of an opaque SecKeyRef).

Having to put data in Keychain just to retrieve the public key bits of a certificate seems really complex, slow and inefficient. On OS X, the key’s’ data can be retrieved by just calling SecCertificateCopyValues() on the certificate with kSecOIDX509V1SubjectPublicKey as the list of OIDs.

I had to implement both techniques in TrustKit within the pinning logic, which shows how different the code is depending on the platform.

See the bug on Open Radar.

Extracting the SPKI data from a certificate

On both iOS and OS X, there is no API to extract the Subject Public Key Info bits. On OS X, the SecCertificateCopyValues() function seemed like a good candidate in combination with the kSecOIDX509V1SubjectPublicKeyAlgorithm and kSecOIDX509V1SubjectPublicKeyAlgorithmParameters OIDs. However the function only returns a parsed output (such as the OID corresponding to the key algorithm) instead of the actual bytes of the SPKI.

Overall, the possible solutions here are far from ideal:

  • Parsing ASN1 manually, a non-starter
  • Embedding OpenSSL, which tends to quickly get dangerously outdated

In TrustKit, I took an unsatisfying but less dangerous approach: the developer has to specify the public key algorithm (RSA 2048, ECDSA, etc.) in the configuration. Then, TrustKit re-generates the SPKI by picking the bits corresponding to the algorithm from a hardcoded list, and combining it with the public key bits.

See the bug on Open Radar.

Detecting private trust anchors

When implementing SSL pinning, it is useful to not enforce pinning when a private trust anchor (ie. one that was manually added to the OS’ trust store) is detected in the server’s certificate chain. This is needed for allowing SSL connections through corporate proxies or firewalls and it is how Chrome operates in that scenario; stopping attackers with the ability to modify the OS (by adding certificates to the OS trust store, hooking SSL APIs, etc.) is not part of SSL pinning’s threat model.

There are no APIs on iOS to differentiate private trust anchors from system trust anchors (the ones shipped with the OS). Consequently, pinning can only be implemented in a way that will always block connections where pinning should not actually be enforced.

On OS X, the list of private trust anchors can be retrieved using SecTrustSettingsCopyCertificates() with the kSecTrustSettingsDomainUser and kSecTrustSettingsDomainAdmin domain settings. Then, this list can be used to detect a private trust anchor in the server’s certificate chain, as implemented in TrustKit.

See the bug on Open Radar.

August 11, 2015
ios

Introducing SSL Kill Switch 2

I recently started working on iOS SSL Kill Switch again, a tool which disables SSL validation and pinning in iOS Apps. I’ve implemented some significant changes including enabling support for OS X Apps, as well as adding the ability to disable TrustKit (the SSL pinning library I released at Black Hat).

To reflect all these changes, I’ve renamed the tool to “SSL Kill Switch 2” and moved the repository to a new GitHub page; the old repository will no longer be maintained.

August 08, 2015
ios, ssl

TrustKit: Effortless SSL Pinning for iOS and OS X

Earlier this week, Angela Chow (from the Yahoo security team), Eric Castro and I spoke at the Black Hat US conference in a session titled “TrustKit: Code Injection on iOS 8 for the Greater Good” (slide deck is available here).

We presented TrustKit, a new open-source library that makes it very easy to deploy SSL pinning in iOS or OS X Apps. The approach we used to solve SSL pinning is novel in several ways, as it is based on techniques such as function hooking and code injection, which are generally used for reverse-engineering and customizing Apps on a jailbroken device.

The result is what we like to call “Drag & Drop” SSL pinning: TrusKit can be deployed in any App in minutes, without having to modify the App’s source code.

Additionally, to ensure TrustKit would work in real-world Apps with millions of users and strict requirements in terms of performance, battery life and overall usability, we collaborated with the Yahoo mobile and security teams. They helped us design a library that would solve the challenges most companies are facing with SSL pinning, such as detecting how many users are seeing unexpected certificate chains in the wild, or ensuring that 100% of the App’s connections are protected.

TrustKit is already live on the App Store in some of the Yahoo Apps, and we open-sourced the project on GitHub at the end of our presentation. Have a look and let us know what you think! The more developers use it, the better it will get.

August 08, 2015
ios, ssl

Security and Privacy Changes in iOS 9

I just watched the WWDC 2015 sessions about security and privacy and put together some notes about the changes brought by iOS 9 that I thought were interesting.

App Transport Security

This is a big one: by default on iOS 9, Apps will no longer be allowed to initiate plaintext HTTP connections, and will be required to use HTTPS with the strongest TLS configuration (TLS 1.2 and PFS cipher suites):

It is possible to lift these restrictions and still retrieve data over plaintext HTTP by adding some configuration keys to the App’s Info.plist. Also, App Transport Security seems to only be available for connections initiated using NSURLSession. While NSURLConnection is being deprecated (forcing everyone to switch to NSURLSession for HTTP), I wonder if plaintext connections initiated through other network APIs (such as NSStream) will fail as well.

A great change overall, and this might even be the first step to mandatory HTTPS as part of the App Store policy.

Detection of Installed Apps Blocked

Apple has closed three privacy gaps that allowed Apps to detect which other Apps were installed on the device.

  • The first technique was to use the sysctl() function to retrieve the process table (a remnant of OS X), which includes the list of running Apps. In iOS 9, sysctl() was modified to no longer allow sandboxed Apps to retrieve information about other running processes.

  • The second technique relied on the UIApplication canOpenUrl method to try known URI schemes implemented by specific Apps, in order to detect if these Apps were installed on the device. This was made famous by Twitter, which used a list of 2500 URI schemes to detect which Apps were installed on the device. In iOS 9, Apps have to explicitly declare which schemes they would like to use in their Info.plist file. For Apps targeting iOS 8 but running on an iOS 9 device, there is also a hard limit of 50 URI schemes that can be checked at most.

  • There was a third technique which relied on the icon cache being accessible to sandboxed Apps. Although it wasn’t even mentionned in the WWDC video, this privacy leak has also been addressed in iOS 9.

Overall, closing these privacy gaps is a great move for users as these APIs were being abused by various Apps and analytics/ads SDKs.

Universal Links are meant to replace URI schemes for communicating with other Apps. In a nutshell, an App can specify a list of web domains they’re associated with:

Then, if the App is installed on the device, the App will have the ability to “receive” and open any HTTP or HTTPS link to the associated domains, when the link gets clicked on the device. If the App is not installed, the link will be opened in Safari instead.

This new mechanism is better than URI schemes for a few reasons:

  • Strong ownership of links/content: enabling Universal Links requires uploading a specific file to the web domain to be associated with the App, which proves ownership. Conversely, a URI scheme could be claimed by any App that gets installed on the device.
  • Because Universal Links rely on standard HTTP and HTTPS URLs, no need for specific, iOS-only URLs. Also, the links work regardless of whether the App is installed or not.
  • Better privacy as Universal Links do not leak whether a specific App is installed on the device.

For more information, there’s a full WWDC session dedicated to Universal Links.

Mac Address Randomization Extended

Mac address randomization was introduced in iOS 8 in order to prevent the tracking of users through their device’s WiFi MAC address. This feature was initially criticized for being enabled only if location services were turned off on the device.

To further prevent user tracking, Apple has extended MAC address randomization to additional services, and seems to now include location services scans:

Misc. Keychain Improvements

Apple made some notable changes to the Keychain on iOS 9:

  • The physical store where the Keychain cryptographic data is persisted has been moved to the Secure Enclave, the device’s cryptographic co-processor (available since the iPhone 5S).
  • The weakest Keychain accessibility class, kSecAttrAccessibleAlways will be deprecated in iOS 9. This protection class causes the data to not be encrypted at all when stored in the Keychain and has no real use case as kSecAttrAccessibleAfterFirstUnlock can always be used instead.
  • When using TouchID to protect Keychain items, touchIDAuthenticationAllowableReuseDuration can be used to avoid re-prompting the user for their fingerprint if they already had a match some time ago.

Application Passwords

Keychain items can now be encrypted using both the device’s passcode and an “Application password”; both values are then needed to decrypt and retrieve the item. This allows Apps to control when the data is accessible/decrypted, instead of having the data decrypted as soon as the device is unlocked. Potential use cases include:

  • Allowing the user to configure an App-specific passcode to protect the user’s authentication token or encryption key; the App would then be able to prompt the user for their App passcode on launch.
  • Storing the Application password on a server and having the App retrieve the password after a successful authentication.
  • Having the Application password being displayed via a hardware token.

Application passwords can be configured using the LACredentialType.ApplicationPassword setting.

Secure Enclave Protected Private Keys

SecGenerateKeyPair(), which is used to generate RSA and ECDSA key pairs, can now be configured to directly store the generated private key in the device’s Keychain (within the Secure Enclave). The key can then be subsequently used to sign data or verify signatures directly within the Secure Enclave by specifying additional parameters to SecKeyRawSign() and SecKeyRawVerify(). This means that the private key can be used without ever leaving the device’s Secure Enclave. The kSecAttrTokenIDSecureEnclave attribute needs to be used when generating the key pair.

Network Extension Points

Extensions were introduced in iOS 8, and are built around the concept of “extension points”: specific areas of the OS that can be customized using a designed API. Extension points from iOS 8 include for example “Custom Keyboard” and “Share”, respectively for customizing the device’s keyboard and adding new share buttons to receive content from other Apps. iOS 9 brings several new extension points geared toward network filtering and VPNs:

  • The Packet Tunnel Provider extension point, to implement the client-side of a custom VPN tunneling protocol.
  • The App Proxy Provider extension point, to implement the client-side of a custom transparent network proxy protocol.
  • The Filter Data Provider and the Filter Control Provider extension points, to implement dynamic, on-device network content filtering.

These extension points can only be used with a special entitlement, thereby requiring Apple to approve the extension first.

June 16, 2015
ios

Hooking Variadic Functions With Substrate

If you’re used to writing iOS tweaks using theos and the Logos preprocessor directives, you may run into problems when trying to hook methods or functions that accept an arbitrary number of arguments. One example of a variadic method is NSString’s+ (instancetype)stringWithFormat:(NSString *)format, ....

To hook variadic methods or C functions, the Substrate C API has to be used directly. I wrote a quick proof of concept for the function open():

// Hook open() to catch the path of all files being accessed
static int (*original_open)(const char *path, int oflag, ...);

static int replaced_open(const char *path, int oflag, ...) {
    int result = 0;

    // Handle the optional third argument
    if (oflag & O_CREAT) {
        mode_t mode;
        va_list args;

        va_start(args, oflag);
        mode = (mode_t)va_arg(args, int);
        va_end(args);
        result = original_open(path, oflag, mode);
    }
    else {
        result = original_open(path, oflag);
    }
    NSLog(@"OPEN: %s", path);
    return result;
}

void hookLibC(void) {
    MSHookFunction(open, replaced_open, (void **) &original_open);
}

The full code is available as a gist on GitHub.

April 24, 2015
ios