Thursday 20 February 2014

UNIX Domain Sockets - An unusual way for inter app communication -iOS

Yes,they did it again. The penetration testers irritated me once again. This time it is exchanging data between two applications.Initially these were the methods used by me to achieve inter app communication:

1.Use Keychain - Age old technique of encrypting the data with a salted SHA -256 algorithm and storing the data in the keychain.

2. Use UIPasteBoards - Technique uses a customized UIPasteBoard to share data between applications.

There are other techniques as well like URL -Schema , HTML 5 local storage etc. but in my case I had the use-case, where I was not allowed to launch a second app  or a browser from my parent app. Thus I had to rule out a few techniques.Of the remaining techniques that were available- using UIPasteBoards had to be ruled out because my app needed to support jail-broken devices as well. The penetration testers were able to access the binary property list file created by UIPasteBoards - /private/var/mobile/Library/Caches/com.apple.UIKit.pboard/pasteboardDB
in a jail Broken devices-consequently the app was exposed. 

I held a strong opinion that the keychain is virtually unbreakable,but it proved inadequate as well.The penetration testers used a app called Snoop -it  for a jailbroken device and the app was exposed again.Here is a basic idea about how it's done - http://resources.infosecinstitute.com/ios-application-security-part-12-dumping-keychain-data/

This is where I discover a very obscure technique called UNIX domain sockets. Here is what wiki describes Unix domain sockets as -http://en.wikipedia.org/wiki/Unix_domain_socket

This is the way we achieved inter app communication using Unix Domain Sockets :

1.Creating and Opening a Unix domain socket:


#define SOCKET_PATH "/var/mobile/Library/AddressBook/SOCKET"
int serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
if (serverSocket < 0) {
  perror("socket");
  exit(EXIT_FAILURE);
}
// Ensure that SOCKET_PATH does not exist
unlink(SOCKET_PATH);
struct sockaddr_un address;
address.sun_family = AF_UNIX;
strcpy(address.sun_path, SOCKET_PATH);
socklen_t addressLength = SUN_LEN(&address);
if (bind(serverSocket,
         (struct sockaddr *)&address,
         addressLength) != 0) {
  perror("bind");
  exit(EXIT_FAILURE);
}
if (listen(serverSocket, 5) != 0) {
  perror("listen");
  exit(EXIT_FAILURE);
}
int connection = 0;
while ((connection = accept(serverSocket,
                            (struct sockaddr *)&address,
                            &addressLength)) >= 0) {
  NSString *message = @"This is a message from the other program.";
  NSMutableData *messageData =
    [[[message dataUsingEncoding:NSUTF8StringEncoding] mutableCopy]
     autorelease];
  write(connection,
        [messageData mutableBytes],
        [messageData length]);
  close(connection);
}
close(serverSocket);
// Remove socket at SOCKET_PATH
unlink(SOCKET_PATH);

Whenever an app performs interprocess communication via Unix domain Sockets one of the app must run in the background.The application in the background can can initiate a long running task by using beginBackgroundTaskWithExpirationHandler:
This is how the app which created the socket using the above code starts the long running task when it enters background:

// Begin async execution in background. ‘socketTaskID’ (of type // UIBackgroundTaskIdentifier) is an instance variable of the // current class.
UIApplication *sharedApp = [UIApplication sharedApplication]; socketTaskID = [sharedApp
                beginBackgroundTaskWithExpirationHandler:^(void) {
  [sharedApp endBackgroundTask:socketTaskID];
   socketTaskID = UIBackgroundTaskInvalid;
}];
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Listen for incoming connections -> See Listing 4-10
// ...
  [sharedApp endBackgroundTask:socketTaskID];
  socketTaskID = UIBackgroundTaskInvalid;
});

Now the second app which receives the data comes to the foreground - we have already send our app that has created the socket to the background.This is how the second application receives string from the first application via the socket already created:


#define SOCKET_PATH "/var/mobile/Library/AddressBook/SOCKET"
int clientSocket = socket(AF_UNIX, SOCK_STREAM, 0);
if (clientSocket < 0) {
  perror("socket");
  exit(EXIT_FAILURE);
}
struct sockaddr_un address;
address.sun_family = AF_UNIX;
strcpy(address.sun_path, SOCKET_PATH);
socklen_t addressLength = SUN_LEN(&address);
if (connect(clientSocket,
            (struct sockaddr *)&address,
            addressLength) != 0 ) {
  perror("connect");
  exit(EXIT_FAILURE);
}
// Receive the string.
NSMutableData *receivedData = [[[NSMutableData alloc]
                                initWithLength:100]
                               autorelease];
read(clientSocket,
     [receivedData mutableBytes],
     [receivedData length]);
NSString *receivedMessage = [[[[NSString alloc]
                              initWithData:receivedData
                              encoding:NSUTF8StringEncoding]
                             autorelease];
NSLog(@"Received: %@", receivedMessage);
[receivedData release];
// Close the connection.
close(clientSocket);

There we go, we have managed to set up an inter app communication using Unix sockets which is difficult for penetration testers to crack - at least for the time being.I guess the only limitation of this method is that is uses a  common directory - /var/mobile/Library/AddressBook/ for both the communicating applications.
Wish my readers many many happy hours of Coding....






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....