Penetration Testing for iPhone Applications — Part 6
In Part 1 of the article we have discussed about the iPhone application traffic analysis. Part 2, Part 3, and Part 4 covered in-depth analysis of insecure data storage locations on the iPhone. Part 5 covered runtime analysis basics with Cycript. In this part we will take a look at in-depth analysis of application runtime, using Cycript and GNU debugger.
Runtime Analysis with Cycript
With Cycript, we can hook into the application runtime, access and modify the instance variables, invoke the instance methods, and override the existing methods. In the previous article, we discussed how to access instance variables from the application runtime. In this article, we will take a look at how to invoke & override the application instance methods.
Invoking the Instance Methods with Cycript
Cycript allows invoking the application instance methods directly from runtime. This helps in bypassing the validation mechanisms implemented in applications. For the demo, I am using a Photo Vault application; you can download the IPA here. The Photo Vault application will help to keep the photos secure by protecting with a passcode. When the application is launched for the first time, it prompts the user to set a passcode. Later on, the user has to enter the correct passcode to view the private photos. The steps below explain how to bypass the Photo Vault passcode protection by using Cycript.
1. Launch the Photo Vault application and it prompts for a passcode.
2. Connect to the iPhone over SSH and find the application process id, using the ps ax command.
3. Hook into the application process, using the cycript –p [PID] command.
4. At the cycript prompt, grab the application delegate instance, using the UIApp.delegate command.
5. Obtain the photovault class dump by using class_dump_z. Search the class dump for AppDelegate and
look for the @interface of it.
6. Looking at the class dump reveals an interesting method called pinLockControllerDidFinishUnlocking. The method does not take any arguments. So we can invoke it directly, using [UIApp.delegate pinLockControllerDidFinishUnlocking] command.
7. Bingo! It logs you into the application without entering the passcode and gives access to the private photos.
Overriding the Instance Methods with Cycript:
The Objective-C runtime allows modification and replacement of the existing methods code dynamically. This technique is known as method swizzling. For this exercise, I have created a demo application called HackTest; you can download the IPA here. The HackTest application prompts the user for a password and displays the welcome screen upon entering the correct password. The steps below explain how to bypass the HackTest password validation mechanism by overriding the existing methods by using Cycript.
1. Launch the HackTest application and it prompts for a password.
2. Enter any value (ex: abcd) and click on the Enter button. It displays invalid passcode message.
3. Connect to the iPhone over SSH and grab the application process id, using the ps ax command.
4. Hook into the application process, using the cycript –p [PID] command.
5. On the cycript prompt, grab the application delegate instance, using the UIApp.delegate command.
6. Obtain the HackTest class dump, using class_dump_z. Looking at the class dump of HackTest application reveals an interesting method called validatePasscode. The method takes no arguments and returns a Boolean value. The application probably uses this method to check for a valid passcode and takes the authentication decision based on its return value. Now, using the method swizzling technique, we will modify the validatePasscode method to always return true.
7. validatePasscode method is declared in the ViewController interface and the ViewController instance is present in the AppDelegate interface. So grab the ViewController instance using the UIApp.delegate.viewController command.
8. To override the validatePasscode method, run this command:
UIApp.delegate.viewController->isa.messages['validatePasscode']=function (){return 1;}.
It overrides the function to always return 1. isa is a pointer to the class structure and gives access to the method implementation.
9. Bingo! It logs you into the application without entering the password and displays the welcome screen.
The above examples show how one can easily manipulate the runtime and circumvent the security checks implemented in iOS applications using Cycript.
Runtime Analysis with GDB
GDB, gnu debugger, is a powerful tool to analyze and alter the runtime behavior of iOS applications. Debugging is an interactive process and allows setting breakpoints in the application execution, which in turn gives more control over the application. For this exercise, I am using a Photo Vault application; you can download the IPA here. The Photo Vault application keeps the private photos securely by protecting with a password. When the application is launched for the first time, it prompts the user to set a password. Later on, the user has to enter the correct password to access the private photos. Below steps explains how to grab the Photo Vault password from runtime by using GDB.
1. Launch the Photo Vault application and it prompts for a password.
2. Connect to the iPhone over SSH and find the application process id, using ps ax command.
3. Attach GDB to the application process, using gdb –p [pid] command.
GDB attaches to the process and reads the symbols for shared libraries.
4. Obtain the Photo Vault class dump using class_dump_z. Looking at the class dump reveals an interesting method called btnLoginClicked. This method takes no arguments and probably gets invoked upon pressing the login button. Let's inspect the method by setting a breakpoint.
5. Set a breakpoint for the btnLoginClicked method, using b or break command. A breakpoint pauses the program execution whenever a certain point in the program is reached.
6. At this point the application gets frozen and does not accept any input. In order to continue the execution, type c at the gdb prompt.
7. Enter some value in the Photo Vault password and click on the login button.
8. As expected, it hits the breakpoint in GDB and the application execution is paused.
9. At this point, disassemble the btnLoginClicked method to look at the next executable instructions, using the disas or disassemble command. As iPhone binaries are compiled for ARM processors, GDB displays the ARM-based assembly instructions.
10. In the disassembled code, you will see a lot of dyld_stub_objc_msgSend instructions. Objective-C is based on the messaging framework, hence methods aren't invoked or called; instead messages are passed between objects by using the objc_msgsend method. An attacker can tap into these messages and look for sensitive information. On the GDB terminal, we can set breakpoints for all the objc_msgsend calls and inspect for the important information. As there are too many objc_msgsend calls in the disassembly, it would be difficult and time-consuming process to inspect each and every call. In the btnLoginClicked disassembled code, dyld_stub_objc_msgSend located at 0x0000618a is followed by a cmp instruction (conditional execution), which probably means it checks for a login state. So let's set a breakpoint at 0*0000618a and inspect the objc_msgsend call.
11. Continue the application execution and it hits the breakpoint.
We can inspect the breakpoint by looking at the register values. To see register values at the breakpoint, use the info reg command. iPhone uses ARM-based processors, which contain 15 general purpose registers (r0 – r14), a program counter (r15), and a current program status register (flags). The standard ARM calling conventions allocates the 16 ARM registers as:
r0 to r3—holds argument values passed to a method (r0 is also used to store the return result from a method) r4 to r11
r12 ( ip)—intra-procedure-call scratch register
r13 ( sp)—stack pointer
r14 (lp)—link register
r15 (pc)—program counter
objc_msgsend method contains 3 arguments:
Receiver (r0)—A pointer to the called object. That is, the object on which the method should be invoked. x/a $r0 command is used to examine the address located at r0 and po $r0 command is used to print the object located at r0.
Selector (r1)—String representation of the method that handles the message. The x/s $r1 command is used to examine the string value stored in r1 register.
The first argument passed to the objc_msgsend is stored in r2. Po $r2 command is used to print the r2 register value.
Looking at the register values explains that r0 contains the original password and the application is trying to compare it with the user entered value in r2. Bingo! Now we know the password and can log into the application directly.
The above example shows how one can easily manipulate the application runtime using GDB. The application security can be improved further by adding anti-debugging techniques that would prevent malicious users from using a debugger. Though they won't provide 100% security, it works as an additional layer of security and delays the attacker attempts.
11 courses, 8+ hours of training
Penetration Testing For iPhone Applications is going to be covered in a series of articles. In Part 7, we will take a look at push notifications, URL schemes and few automation tools.