Wednesday 19 February 2014

Mobile Security - Save your iOS- app data from Burp Suite

Recently I had bad time with penetration testers. They orchestrated a MIM (Man In the Middle) attack on the iOS -app that we were developing and were able to intercept all our sensitive data.
For those of you who are have never dealt with penetration testers, they can be a real pain. This is how they made a MIM attack:

1.App interacts with a server - Ngenix to interchange data.
2.Using Burp-Suite the app was fooled to believe that a intermediate server was the Ngenix server.
3.App interacts with Ngenix server - Data is intercepted.

Here is a link to demonstrate the Burp-Suite capability:
http://www.tuaw.com/2011/02/21/how-to-inspect-ioss-http-traffic-without-spending-a-dime/

After knitting brows for a long time we finally cracked it. For those of you out there who still have their brows knitted, here's some help.
Algorithm to stop Burp Suite redirection to fake server.

1.Install a signed certificate to Ngenix server - Our's was an AlphaSSL certificate.This needs to be done only once on the server end.
2.AlphaSSL gives a client certificate as well. At the client end we had put the client certificate in our documents directory- Ideally save the client certificate in KeyChain.
3.In order to confirm that the app is interacting with Ngenix and not some MIM attacker's server- We match the fingerprint of our client certificate with  the fingerprint of the server certificate.If these fingerprints match  only then communications are allowed .
4.Request parameters are encrypted with a pre- defined algorithm - either SHA -251 or SHA -1 and are sent to the server.
5.The Ngenix server logic decrypts the parameter and sends the corresponding encrypted response.

Here are some key code snippets:

1. Generating Client FingerPrint:

-(NSString*)generateClientFingerPrint
{
    NSString *certificateName = [NgenixLib returnValueForKey:@"certificateName" ];
    NSLog(@"The certificate name is :::::::::%@",certificateName);
    NSString *rootCertPath = [[NSBundle mainBundle] pathForResource:certificateName  ofType:@"cer"];    
NSData *rootCertData = [NSData dataWithContentsOfFile:rootCertPath];
   //Code to generate the fingerPrint
    NSLog(@"root Data=%@",[self sha1:rootCertData]);
    NSString *clientFingerPrint = [NSString stringWithString:[self sha1:rootCertData]];
    return clientFingerPrint;

}

-(NSString*)sha1:(NSData*)certificatData {
    unsigned char sha1Buffer[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(certificatData.bytes, certificatData.length, sha1Buffer);
    NSMutableString *fingerprint = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 3];
    for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; ++i)
        [fingerprint appendFormat:@"%02x ",sha1Buffer[i]];
    return [fingerprint stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

}

2.Verifying the Server :

- (void)connection:(NSURLConnection *)connection  didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    //This is where the action truely lies!!
    //This delegate method gets the public key from the certificate
    //Gets the fingerprint from the certificate and notifies that the
    //Fingerprint has been generated
   NSString* trustUrl = [NgenixLib returnValueForKey:@"trustUrl"];
    NSLog(@"The trustUrl++++++%@",trustUrl);
    NSArray *trustedHosts = [NSArray arrayWithObject:trustUrl];
    BOOL isAuthMethodServerTrust = [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
    NSLog(@"<%p %@: %s line:%d> Result:%s", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __PRETTY_FUNCTION__, __LINE__, (isAuthMethodServerTrust == YES) ? "YES" : "NO");
    if (isAuthMethodServerTrust)
    {
        NSLog(@"####### =%@",challenge.protectionSpace.host);
        if ([trustedHosts containsObject:challenge.protectionSpace.host])
        {
            NSLog(@"<%p %@: %s line:%d> trustedHosts containsObject:challenge.protectionSpace.host", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __PRETTY_FUNCTION__, __LINE__);
            NSURLCredential* urlCredential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            NSLog(@"<%p %@: %s line:%d> Url credential", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __PRETTY_FUNCTION__, __LINE__);
            [challenge.sender useCredential:urlCredential forAuthenticationChallenge:challenge];
            
            //Code to verify server certificate info
            //The server’s SSL transaction state, or nil
            //if the authentication method of the protection space is not server trust.
            
            SecTrustRef trustRef = [[challenge protectionSpace] serverTrust];
            CFIndex count = SecTrustGetCertificateCount(trustRef);
            NSLog(@"The count is =%ld",count);
            //for (CFIndex i = 0; i < count; i++)
            
                SecCertificateRef certRef = SecTrustGetCertificateAtIndex(trustRef, 0);
                CFStringRef certSummary = SecCertificateCopySubjectSummary(certRef);
               
                //This is where the server certificate data is finally collected.This method
                //Returns a DER representation of a certificate given a certificate object.
                certData = SecCertificateCopyData(certRef);
                
                NSLog(@"<%p %@: %s line:%d> Certificate summary:%@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __PRETTY_FUNCTION__, __LINE__, (__bridge NSString*) certSummary);

                
                //Calling SecTrustCopyPublicKey method to get the public Key of the server
                SecKeyRef status =  SecTrustCopyPublicKey(trustRef);
                NSLog(@"<%p %@: %s line:%d> Certificate data:%@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __PRETTY_FUNCTION__, __LINE__, (__bridge NSString*) status);
            
                //Getting the SHA1 finger print from the server
                //The CFDataRef type is converted to data and is send over
                //For generating the fingerPrint
                //NSLog(@"*********%@",(__bridge NSData *)(certData));
               NSString * fingerPrint = [self sha1:(__bridge NSData *)(certData)];
                NSLog(@"the FingerPrint is =%@",fingerPrint);
               
                //Saving the user certificate in KeyChain
                //[self saveCertificate];
                //getting the client fingerprint
              NSString* clientFingerPrint =  [NSString stringWithString:[self generateClientFingerPrint]];
                if([fingerPrint isEqualToString:clientFingerPrint])
                {
                    connectionResult = YES;
                    //Posting a notification that the FingerPrint has been obtained
                    //After a previous notification is removed
                    [[NSNotificationCenter defaultCenter] postNotificationName:@"HandleFingerPrintReceived" object:nil];

                }
                else{
                    UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"Alert!!" message:@"The Certificate is invalid,this is a SECURITY THREAT!!The application will exit now" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
                    [alertView show];
                    }
                        
        }
    }
    
}
These are basically the main methods to handle server verification.I will post the  complete source code soon. Keep watching this space ....
Happy Coding....



2 comments: