Skip to main content

iOS Backups

iOS has a backup feature that copies the data on the device to either the host computer or iCloud. The backup may include sensitive data that is on the device, and this should be checked at various stages of your testing.

For user installed iOS applications, everything in the Documents/ directory and the Library/Application Support/ directory are automatically backed up by default. These directories are in the application Data directory.

While these are included by default, the developer may exclude specific files from the backup using the NSURLIsExcludedFromBackupKey: method. Additionally, the use of the Data Protection APIs could be implemented to further restrict a file from being included in a backup. (See the Data Protection procedures for more information).

Creating A Backup

We will use the idevicebackup2 utility so that it is cross-platform. Create a backup of the device so we can test for sensitive data exposure.

First, you much create the directory to store the backups. In this example, I just created a directory called backups. This must exist before the backup command:

% mkdir backups

% idevicebackup2 backup --full backups
Backup directory is "backups"
Started "com.apple.mobilebackup2" service on port 51748.
Negotiated Protocol Version 2.1
Starting backup...
Enforcing full backup from device.
Backup will be unencrypted.
Requesting backup from device...
Full backup mode.
[= ] 1% Finished

The backup is stored in the backups directory under the device UUID (which I have partially obscured):

% ls backups
7dc0b8139123xxxxxxxda6bf6febf8728d803c57

Note: on macOS, ensure that the Terminal application can access the backups directory!

Review Backup Files

In true Apple fashion, they store the backup files in an odd, esoteric format. However, for our purposes, there are a few ways we can see which files from the app are included in the backup. In the backup directory you will find many sub-directories consisting of two (2) characters. Additionally, there are some PLIST files and a Database.

First, we will check to ensure that backup is not encrypted:

% /usr/libexec/PlistBuddy -c 'Print :IsEncrypted' Manifest.plist
false

Next, check that your application was included in the backup:

% /usr/libexec/PlistBuddy -c 'Print :Installed\ Applications' Info.plist
Array {
io.stinger.dvia2
com.example.appname
io.stinger.uncover
}

If the app is included in the backup, get a listing of the files that are included in the backup:

% sqlite3 Manifest.db "SELECT relativePath FROM Files WHERE domain LIKE '%com.example.app%'" | sort -u

We need to understand how the Manifest.db database is storing this information first. We can get this from the database schema.

% sqlite3 Manifest.db ".schema"
CREATE TABLE Files (fileID TEXT PRIMARY KEY, domain TEXT, relativePath TEXT, flags INTEGER, file BLOB);

ColumnDescription
fileIDthe primary key which appears to be a hash of something…
domainthe backup domain such as AppDomain
relativePaththe file path and name
flagsthe flags used to store the file
fileA PLIST of the filename’s metadata

Viewing the files that are stored in the backup is not a straight-forward process. But for our purposes we can get a good idea of the data in the files.

For instance, let’s say that the Cookies file is present in the backup at Library/Cookies/Cookies.binarycookies. We can get the fileID of that file from the Manifest.db database and then run strings against it.

% sqlite3 Manifest.db "SELECT fileID FROM Files WHERE domain LIKE '%com.example.app%' AND relativePath LIKE '%Cookies%'"
b686664633b48b87039bb4844031cbf92b8722d4
46ad03aaf24c35e671d1ba6a12453be616935408

This returns two (2) locations where the Cookies.binarycookies file for our app could be stored. To view these files, use the following directory structure:

The returned location looks like this: 46ad03aaf24c35e671d1ba6a12453be616935408

Use the first two (2) characters as the directory, then the full string as the filename.

% strings 46/46ad03aaf24c35e671d1ba6a12453be616935408 
cook
Asso.godaddy.com
akm_lmprb-ssn
0aPNHuBZshuZMDqcGTizyWISgppRWS4UQjditgKKDFBrIkDntcSlIxlKgll2YIinSIRbBL7JK7x0hhvO87sSFQChQUzQ7QOhXhRmb7GO4ruycRYoOuFFlnsjw9i5XEA0tzno
Asso.godaddy.com
akm_lmprb
0aPNHuBZshuZMDqcGTizyWISgpplIxlKgll2YIinSIRbBL7JK7x0hhvO87sSFQChQUzQ7QOhXhRuKTdcKh2puMgmb7GO4ruycRYpOxppDvRoOuFFln5XEA0tzno

Depending on the data, you may need to use other ways to see the data.

% xxd 46/46ad03aaf24c35e671d1ba6a12453be616935408 
00000000: 636f 6f6b 0000 0003 0000 01f2 0000 0085 cook............
00000010: 0000 0389 0000 0100 0200 0000 1400 0000 ................
00000020: 0501 0000 0000 0000 f100 0000 0000 0000 ................
00000030: 2500 0000 0000 0000 3800 0000 4800 0000 %.......8...H...
00000040: 5600 0000 5800 0000 0000 0000 0000 0000 V...X...........
00000050: 0000 80c8 f13e c441 0000 8008 493e c441 .....>.A....I>.A
00000060: 7373 6f2e 676f 6461 6464 792e 636f 6d00 sso.godaddy.com.
00000070: 616b 6d5f 6c6d 7072 622d 7373 6e00 2f00 akm_lmprb-ssn./.
00000080: 3061 504e 4875 425a 7368 755a 4d44 7163 0aPNHuBZshuZMDqc
00000090: 4754 697a 7957 4953 6770 7052 5753 3455 GTizyWISgppRWS4U
000000a0: 0000 0389 0000 0100 0200 0000 1400 0000 ................
000000b0: 7463 536c 4978 6c4b 676c 6c32 5949 696e tcSlIxlKgll2YIin
000000c0: 5349 5262 424c 374a 4b37 7830 6868 764f SIRbBL7JK7x0hhvO
000000d0: 3837 7353 4651 4368 5155 7a51 3751 4f68 87sSFQChQUzQ7QOh
000000e0: 5868 5275 4b54 6463 4b68 3270 754d 676d XhRuKTdcKh2puMgm
000000f0: 6237 474f 3472 7579 6352 5970 4f78 7070 b7GO4ruycRYpOxpp
00000100: 4476 526f 4f75 4646 6c6e 736a 7739 6935 DvRoOuFFlnsjw9i5
00000110: 5845 4130 747a 6e6f 00ed 0000 0000 0000 XEA0tzno........

If you want to view the file metadata that is stored in the Manifest.db, you can look at it this way:

% sqlite3 Manifest.db "SELECT writefile('cookie.plist', file) FROM Files WHERE fileID='46ad03aaf24c35e671d1ba6a12453be616935408'"

This will produce a file called cookie.plist in the current directory. To view it:

% plutil -p cookie.plist
{
"$archiver" => "NSKeyedArchiver"
"$objects" => [
0 => "$null"
1 => {
"$class" => <CFKeyedArchiverUID 0x600001408180 [0x7ff85c3b2d80]>{value = 3}
"Birth" => 1657559697
"Flags" => 0
"GroupID" => 501
"InodeNumber" => 1430558
"LastModified" => 1657559697
"LastStatusChange" => 1657559697
"Mode" => 33188
"ProtectionClass" => 3
"RelativePath" => <CFKeyedArchiverUID 0x6000014081a0 [0x7ff85c3b2d80]>{value = 2}
"Size" => 1643
"UserID" => 501
}
2 => "Library/Cookies/Cookies.binarycookies"
3 => {
"$classes" => [
0 => "MBFile"
1 => "NSObject"
]
"$classname" => "MBFile"
}
]
"$top" => {
"root" => <CFKeyedArchiverUID 0x6000014080a0 [0x7ff85c3b2d80]>{value = 1}
}
"$version" => 100000
}

Archive Backups

Since the backups that we created were not encrypted, they should be removed – especially if sensitive data was found. Make sure to keep any artifacts that you need for reporting, and if desired zip the backup into an encrypted archive.

Encrypted zip file (with device attached to host):

% zip -er backup.zip ~/Library/Application\ Support/MobileSync/Backup/${idevice_id -l}
Enter password:
Verify password:
adding: 61/ (stored 0%)