Tags:
This is a guest post from security researcher Nitesh Dhanjani. Nitesh will be giving a talk on "Hacking and Securing Next Generation iPhone and iPad Apps" at SANS AppSec 2011.
Many iOS applications use HTTP to connect to server side resources. To protect user-data from being eavesdropped, iOS applications often use SSL to encrypt their HTTP connections.
In this article, I will present sample Objective-C code to illustrate how HTTP(S) connections are established and how to locate insecure code that can leave the iOS application vulnerable to Man in the Middle attacks. I will also discuss how to configure an iOS device to allow for interception of traffic through an HTTP proxy for testing purposes.
A Simple App Using NSURLConnection
The easiest way to initiate HTTP requests in iOS is to utilize the NSURLConnection class. Here is sample code from a very simple application that takes in a URL from an edit-box, makes a GET request, and displays the HTML obtained.
Please note that the code in this particular example is mostly from Apple's wonderful tutorial on how to use NSURLConnection
//This IBAction fires when the user types in a URL and presses GO - (IBAction) urlBarReturn:(id)sender { //htmlOutput is the UITextView that displays the HTML htmlOutput.text=@""; //urlBar is the UITextField that contains the URL to load NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:urlBar.text] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; if(!theConnection) htmlOutput.text=@"failed"; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { //receivedData is of type NSMutableData [receivedData setLength:0]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [receivedData appendData:data]; NSString *tempString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; htmlOutput.text = [NSString stringWithFormat:@"%@%@",htmlOutput.text,tempString]; [tempString release]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { [connection release]; [receivedData release]; NSLog(@"Connection failed! Error: %@ %@", [error localizedDescription], [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]); htmlOutput.text=[NSString stringWithFormat:@"Connection failed! Error %@ %@",[error localizedDescription], [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]); [connection release]; [receivedData release]; }
The result is a simple iOS application that fetches HTML code from a given URL.
In the screen-shot above, notice that the target URL is https. NSURLConnection seamlessly establishes an SSL connection and fetches the data. If you are reviewing source code of an iOS application for your organization to locate security issues, it makes sense to analyze code that uses NSURLConnection. Make sure you understand how the connections are being inititated, how user input is utilized to construct the connection requests, and if SSL is being used or not. While you are at it, you may also want to watch for NSURL * in general to include invocations to objects of type NSHTTPCookie, NSHTTPCookieStorage, NSHTTPURLResponse, NSURLCredential, NSURLDownload, etc.
Man in the Middle
74.125.224.49 is one of the IP addresses bound to the host name www.google.com. If you browse to https://74.125.224.49, your browser should show you a warning due to the fact that the Common Name field in the SSL certificate presented by the server (www.google.com) does not match the host+domain component of the URL.
As presented in the screen-shot above, Safari on iOS does the right thing by warning the user in this situation. Common Name mis-matches and certificates that are not signed by a recognized certificate authority can be signs of a Man in the Middle attempt by a malicious party that is on the same network segment as that of the user or within the network route to the destination.
The screenshot above shows what happens if we attempt to browse to https://74.125.224.49 using our sample App discussed earlier: the connection:didFailWithError: delegate is called indicating an error, which in this case warns the user of the risk and terminates.
This is fantastic. Kudos to Apple for thinking through the security implications and presenting a useful warning message to the user (via NSError).
Unfortunately, it is quite common for application developers to over-ride this protection for various reasons: for example, if the test environment does not have a valid certificate and the code makes it to production. The code below is enough to over-ride this protection outright:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace isEqualToString:NSURLAuthenticationMethodServerTrust]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; }
The details on this code is available from this stackoverflow post. There is also a private method for NSURLRequest called setAllowsAnyHTTPSCertificate:forHost: that can be used to over-ride the SSL warnings but Apps that use it are unlikely to get through the App store approval process (Apple prohibits invocations of private API).
If you are responsible for reviewing your organization's iOS code for security vulnerabilities, I highly recommend you watch for such dangerous design decisions that can put your client's data and your company's data at risk.
Intercepting HTTPS traffic using an HTTP Proxy
As part of performing security testing of applications, it is often useful to intercept HTTP traffic being invoked by the application. Applications that use NSURLConnection's implementation as-is will reject your local proxy's self-signed certificate and terminate the connection. You can get around this easily by implanting the HTTP proxy's self-signed certificate as a trusted certificate on your iOS device [Note: This is not a loop-hole against the precautions mentioned above: in this case we have access to the physical device and are legitimately implatining the self-signed certificate].
If you are using Burp Proxy or Charles Proxy, all you need to do is place the self-signed cert on a HTTP location and browse to it. Instructions for Burp Proxy and instructions for Charles Proxy are available on their respective web sites.
Once you have your iOS device or simulator setup using the self-signed certificate of your HTTP proxy, you should be able to intercept HTTPS connections that would otherwise terminate. This is useful for fuzzing, analyzing, and testing iOS applications for security issues.
If you liked this post check out SANS' new class on Secure iOS App Development.