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






1 comment: