iOS apps can inadvertently store data insecurely, which can expose users' sensitive information. Here are some practical examples of insecure data storage in iOS apps and how to identify them:
Directory /private/var/containers/Bundle/Application/<UUID>/app.app:
- Application Resources: Resource files required for the application to function, including image files, audio files, user interface (UI) files, icons, and other visual or sound elements used by the application.
- Executable Code: The application's executable and other files related to the application's source code are stored here.
- Info.plist: The Info.plist file that contains crucial information about the application such as name, version, package identifier, permissions, and other settings essential for the application to function.
-> Identify
find /private/var/containers/Bundle/Application/ -iname "<app_name>.app"
Directory /var/mobile/Containers/Data/Application/<UUID>:
- Application Documents: Files generated, saved or downloaded by the application. This includes documents, images, videos, or any other type of file that the application handles and that the user can create or save.
- Databases: Structured data stored in databases (e.g. SQLite) used by the application to store user-specific information, settings, history, or any other persistent data.
- Cache: Temporary data cached by the application to speed up performance or maintain information that can be regenerated.
- Settings and Preferences: Application information, such as settings, user preferences, temporary data, among others.
-> Identify
find /var/mobile/Containers/Data/Application/ -iname "*<app_name>*" 2>/dev/null
They are used to store small amounts of data, such as user settings or application preferences. Main purpose: storage of simple, often non-sensitive data.
-> Example Code
let defaults = UserDefaults.standard
defaults.set("senha123", forKey: "password")
-> Identify
ls -la /var/mobile/Containers/Data/Application/<UUID>/Library/Preferences
.plist files can be used to store structured data such as application settings, startup information, and other settings. Main purpose: static application configurations. -> Example Code
<!-- Exemplo Inseguro: Armazenamento de chave de API em um arquivo Info.plist -->
<key>APIKey</key>
<string>YOUR_API_KEY_HERE</string>
-> Identify 1
ls -la /var/mobile/Containers/Data/Application/<UUID>/Library/Preferences/
-> Identify 2 (Info.plist)
Info.plist is an essential properties file for iOS apps, containing crucial metadata like app name, bundle identifier, version, author, and important settings. Although its main purpose is to describe the characteristics of the application to the operating system and it is not the recommended approach, there is the possibility of storing sensitive data in it.
ls -la /private/var/containers/Bundle/Application/<UUID>/app.app
PList files come in two forms: they can be occasional XML files or encoded in binary format. For PList files that are standard XML, reading can be done directly without the need for any additional tools. However, for those that are in binary format, a tool called plistutil
may be necessary to access and view the stored information.
plistutil -i Info.plist -o Info.plist.xml
It is also possible to use the objection to perform the reading.
ios plist cat {plist_file}
They are used to store large amounts of data, perform complex queries and manage relationships between data. Main purpose: storing structured and complex data.
-> Example Code
// Direct access to sensitive data in SQLite without encryption
let fileURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("appDatabase.sqlite")
let database = try! Connection(fileURL.path)
let users = Table("users")
let email = Expression<String>("email")
let password = Expression<String>("password")
let query = users.select(email, password)
for user in try! database.prepare(query) {
print("email: \(user[email]), password: \(user[password])")
}
-> Identify
ls -la /var/mobile/Containers/Data/Application/<UUID>/Documents/
It is a framework that abstracts the data storage layer and can use an SQLite database behind the scenes. Main purpose: managing data in an object context, facilitating the manipulation and persistence of objects in a database.
-> Example Code
// Direct recovery of sensitive data from Core Data without protection
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
do {
let users = try context.fetch(fetchRequest)
for user in users {
print("Username: \(user.username), Password: \(user.password)")
}
} catch {
print("Error fetching data: \(error.localizedDescription)")
}
-> Identify
ls -la /var/mobile/Containers/Data/Application/<UUID>/Documents/
In an application, it is possible to identify sensitive data leaks by analyzing process memory. To investigate an application's memory, the first step is to create a memory dump.
Use the "fridump" command for this purpose and then apply filters to identify possible sensitive information that has been exposed, the script will create a /dump folder with the information.
https://github.com/Nightbringer21/fridump
-> Dump memory from an iOS device associated with a specific app
python fridump.py -U <app_name>
-> Objection
memory dump all <name.dmp>
*Issue iOS - Untrusted Developer
General -> Profile & Device Management -> Developer App -> Trust "Apple Development ..." -> Trust
Keychain offers developers a secure way to store and access sensitive information, eliminating the need to store this data directly in applications. It uses advanced encryption techniques and security measures to protect stored data, ensuring that only authorized applications and authenticated users can access it.
In addition to storing passwords and keys, Keychain is also capable of generating and storing cryptographic keys, digital certificates, and other types of security credentials. It offers an easy-to-use API for developers to integrate Keychain into their applications, providing a convenient and secure way to handle sensitive information.
-> Extracting keys, certificates and information from the iOS device's Keychain.
https://github.com/ptoomey3/Keychain-Dumper
./keychain_dumper > keychain.txt
-> Extract keys, certificates, and keychain information from a specific application on a device
objection -g <bundle_identifier> explore -s "ios keychain dump"
Applications can store sensitive data in their files and when a backup is made, this data can be inserted. So we can use imazing to perform a backup on our cell phone and check if it contains any sensitive data such as passwords, tokens and so on.
To back up your device, plug it into the USB port and then click on your device which will appear on the iMazing screen. Then click on "Back up":
When the backup procedure is complete, go back to the start screen, click on "Backups" and select the backup of your device. Then click on "File System" and check the files that were inserted into the application's backup. Check for sensitive data, in this case we created a test application and saw a Realm file inserted in the "Documents" folder:
Realm is a mobile database platform designed to be fast and easy to use. Unlike other database solutions, Realm is built from the ground up for mobile devices, which means it is optimized for performance and efficiency in mobile environments. It allows developers to effectively store and manage data locally in applications, offering an intuitive and powerful API. To read this type of file we can use Realm Studio
.
Realm requires us to create a model for the data. An example template would look like this:
import Foundation
import RealmSwift
class PersonRealm : Object {
@objc dynamic var id = 0
@objc dynamic var name = ""
@objc dynamic var age = 0
override static func primaryKey() -> String? {
return "id"
}
}
To interact with our Realm, we can use the following code:
import Foundation
import RealmSwift
class StorageRealm {
public func connection() {
let realm = try! Realm()
// Data entry
insertPerson(realm: realm, name: "Victor", age: 30)
// Data query
let people = fetchPerson(realm: realm)
for person in people {
print("id: \(person.id), name: \(person.name), age: \(person.age)")
}
}
func insertPerson(realm: Realm, name: String, age: Int) {
let person = PersonRealm()
person.id = (realm.objects(PersonRealm.self).max(ofProperty: "id") as Int? ?? 0) + 1
person.name = name
person.age = age
try! realm.write {
realm.add(person)
}
}
}
-> Identify
ls -la /var/mobile/Containers/Data/Application/<UUID>/Documents/
NSURLSession, by default, saves data such as HTTP requests and responses in the Cache.db database. This database can contain sensitive information such as tokens, usernames and other sensitive information that has been cached.
Objection can open and interact with the database with the sqlite connect Cache.db
command, as it is a normal SQLite database. Or you can extract the file and check it manually:
-> Identify
ls -la /var/mobile/Containers/Data/Application/<UUID>/Library/Caches/<Bundle Identifier>