In this article by Swaroop Yermalkar, the author of the book Learning iOS Penetration Testing, we will take a look at insecure data storage vulnerability of iOS applications. We have a good understanding of how to install third party iOS apps, transfer files, and various other concepts about tools and utilities required to pentest.
(For more resources related to this topic, see here.)
SQL injection in iOS applications
Web app pentesters must be familiar with SQL injection where the user input is treated as an arbitrary command due to the lack of input validation. The iOS applications that are using local storage are vulnerable to injection attacks if developers are not sanitizing/escaping user input. The danger here is that the attack is possible on non-jailbroken devices and the local data can be corrupted, this could cause unpredictable behavior in the app when it is relying on this data.
We will use the vulnerable iGoat application to demonstrate this SQL injection attack on its local storage. The iGoat application's source code is available, so let's look into the code using Xcode. In other cases, when you just have binary, you can reverse engineer the application using some techniques to view SQL queries and understand backend application logic.
If you take a look at a SQL query, it is as follows:
NSString *query = [NSString stringWithFormat:@"SELECT title FROM article WHERE title LIKE '%@' AND premium=0", searchString];
Query is taking user input at runtime and executing it without sanitizing, which is vulnerable case for a SQL injection.
Let's execute the following steps to perform SQL injection on iGoat iOS application:
- The iGoat iOS application has an exercise for SQL injection attack. Application has search option that can only be used to search free articles.
- When we select the Search option, it shows the free articles available. There is no option to see any other articles.
- Now, let's insert injection payload as ' or '1'='1 in the search article field.
- Now backend query with our injected payload will be as follows:
NSString *query = [NSString stringWithFormat:@"SELECT title FROM article WHERE title LIKE '' or '1'='1 ' AND premium=0", searchString];
This will break the existing query logic and run true condition as 1=1 for premium articles. See the following screenshot:
- Once payload gets executed, you can see premium articles along with free articles.
Congrats! You just performed a SQL injection in an iOS application, as shown in the following screenshot:
Developers should always perform escaping/sanitizing on user input before proceeding to it. You can make use of parameterized queries, which will prevent attacks similar to the SQL injection.
Insecure storage in Core Data
Core Data is an object relational model that creates a layer between user interface and database. Developers prefer Core Data as they are fast, in terms of record creation, as compared to the traditional SQLite format.
From a security point of view, these files are similar to SQLite with the difference being that the tables have the prefix Z.
In this exercise, let's follow the given steps to find out the sensitive data that is stored in Core Data:
- We will use Core Data iOS app for this exercise. Once you open the application, you will observe following view:
- Let's insert username and password and select the Register option , as follows:
- Now, the records are stored successfully. Let's see whether user credentials are stored securely or not, as shown in the following:
- Now, download Core Data application files from /var/mobile/Constrainers/Data/Application and open the CredentialManager.sqlite file using the SQLite browser, as shown in the following screenshot:
You will observe that all the tables starting with the prefix Z and the credentials are stored in plain text. You can check ZUSERNAME and ZPASSWORD column values.
Though Core Data is easy to use and fast, it should never be used to store sensitive information.
Insecure storage in Keychain
Keychain is a secure location in iOS where data is encrypted and tied to device locking/unlocking. The Keychain database is in an encrypted format and the encryption happens with a unique hardware-specific key. The hardware key that is used for encryption is at a secure location and can't be extracted from the device. Keychain items are classified into five classes: generic passwords (kSecClassGenericPassword), internet passwords (kSecClassInternetPassword), certificates (kSecClassCertificate), keys (kSecClassKey), and digital identities (kSecClassIdentity, identity=certificate + key).
Data protection mechanism is implemented by iOS where the keychain having sensitive data is protected with another layer of encryption and is tied to the user's passcode. Data protection mechanism is designed to protect the user's data in case a device is lost or stolen. So the encryption offered by the data protection API is dependent on the strength of a user's passcode. Again, the problem is that Apple does not enforce the user to set the password. Users can use their devices without passcode. Also, in most cases, the users keep a simple 4-digit password that could be easily cracked with bootrom vulnerability.
There are different conditions on which a developer can decide when a keychain item can be readable by an application. These various conditions are known as data protection accessibility constants that can be classified as follows:
As per the preceding constants, data is accessible when the device is in unlocked condition or when the device is first unlocked after reboot. Developers can also set constant to make the data accessible all the time.
When we take the backup of iPhone using iTunes, the keychain's SQLite database is stored as a PLIST file (Keychain-backup.plist). The iTunes application has option to encrypt the backup and when Keychain items are backed with this option, they can be still moved/loaded to another device. However, if the developer has used accessibility constant as ThisDeviceOnly, the keychain items are protected and cannot be moved to other iOS devices.
Many times, developers make the mistake of storing the application's credentials in the plain text in the keychain or setting the data protection accessibility constants in order to make it available only after unlock. Upon gaining physical access to the device that is not protected with a passcode, an attacker can access all keychain items and dump all with a single command. So, it's never recommended to store the application's credentials/persistent sensitive data in keychain. You can use keychain to store temporary data, for example, user's session token that will expire on logging out.
The problem is that an attacker having access to the rooted device can access all the sensitive information in keychain.
Let's perform following steps to dump keychain data on a jailbroken iDevice:
- Start the KeyChainDemo iOS application and save the credentials as shown in the following screenshot:
- You can get keychain_dumper from GitHub and then use SSH File Transfer Protocol (SFTP) to copy files on iDevice. Once you copy the files, use the #./keychain_dumper command to dump the keychain data, as shown in the following:
- You can observe login details as username and password as keychain_secret in plain text, as shown in the following screenshot:
Keychain is known to be a secure place and requires many prerequisites for attack, such as iDevice should be rooted, unlocked, and so on to view the contents.
Till date, there has been lot of exploits to brute force the passcodes and also jailbreak iDevice. An attacker having access to the device can exploit the vulnerability and dump the keychain data. So developers should avoid storing confidential data in keychain. Hence, we studied different formats to store the data locally.
Developers should avoid storing sensitive information locally. You can store the sensitive information at server side. If the information needs to be stored locally, use the encrypted format of database over plain-text formats.
In this article, we studied different formats of iOS local storage, such as SQLite, PLIST, Keychain, and so on. You also learned how an attacker can easily access the sensitive data if the developers are not aware of possible threats. We then engaged in interesting attacks such as SQL injection on iOS application's local storage. Finally, you came to know about the different ways to conduct security audit of different local data storage formats.
Resources for Article:
- Learning Selenium Testing Tools with Python [article]
- Selenium Testing Tools [article]
- BackTrack 4: Security with Penetration Testing Methodology [article]