Sensitive Data Disclosure
iOS apps routinely store information in SQLite databases, or Properly List (PLIST) files. This could include sensitive information such as credentials, session information, encryption keys, etc. It is always worthwhile to review every file in the both the Bundle and Data directories to see what you can find.
To review the information in SQLite databases, you will use the sqlite3
command which should be on your device. PLIST files can be either XML or binary, but the plutil
command will convert them if necessary.
Some other files will be binary, so you will need to use a tool such as the strings
command to review them.
Property List (plist)
Search for all PLIST files in both the Bundle and Data directories on the device.
# find ${BUNDLEDIR} -type f -exec grep -ali plist {} \;
# find ${DATADIR} -type f -exec grep -ali plist {} \;
To review a specific PLIST file:
# plutil filename.plist
NSKeyedArchiver Data
In many cases during runtime the application will store data in a PLIST file using an Apple API called NSKeyedArchiver. This API is used to serialize data which is then stored in the PLIST file as hex. If you encounter this type of data, you can easily decode it:
. . .
LastUserIdentity = <62706c69 73743030 d4010203 04050607 0a582476
65727369 6f6e5924 61726368 69766572 5424746f 7058246f 626a6563
74731200 0186a05f 100f4e53 4b657965 64417263 68697665 72d10809
5f10104c 61737455 73657249 64656e74 69747980 01a50b0c 13141555
246e756c 6cd30d0e 0f101112 59757365 7249644b 6579586f 72674964
4b657956 24636c61 73738002 80038004 5f100f30 30357130 30303030
30364f42 7a535f10 12303044 71303030 30303030 45416943 454157d2
16171819 5a24636c 6173736e 616d6558 24636c61 73736573 5f101553
46557365 72416363 6f756e74 4964656e 74697479 a21a1b5f 10155346
55736572 4163636f 756e7449 64656e74 69747958 4e534f62 6a656374
00080011 001a0024 00290032 00370049 004c005f 00610067 006d0074
007e0087 008e0090 00920094 00a600bb 00c000cb 00d400ec 00ef0107
00000000 00000201 00000000 0000001c 00000000 00000000 00000000
00000110>;
. . .
Simply copy out the hex data between the <
& the >
and pipe it into a tool that can reverse the hex -- such as xxd
.
echo “{ 62706c69 . . . 00000110 }” | xxd -r -p
bplist00?
X$versionY$archiverT$topX$objects??_NSKeyedArchiver _LastUserIdentity??
YuserIdKeyXorgIdKeyV$class???_005q0000006OBzS_00Dq0000000EAiCEAWZ
$classnameX$classes_SFUserAccountIdentity SFUserAccountIdentityXNSObjec$)
27IL_agmt~????????????
While this is not especially helpful, we can see from the beginning of the output bplist00?
that it is another property list file embedded into a property list file. So, we can further extend our command to automatically decode the binary PLIST data using plutil.
echo “{ 62706c69 . . . 00000110 }” | xxd -r -p | plutil -p -
{
"$archiver" => "NSKeyedArchiver"
"$objects" => [
0 => "$null"
1 => {
"$class" => <CFKeyedArchiverUID 0x7fefc1e08ab0 [0x7fff8b4988c0]>{value = 4}
"orgIdKey" => <CFKeyedArchiverUID 0x7fefc1e08a90 [0x7fff8b4988c0]>{value = 3}
"userIdKey" => <CFKeyedArchiverUID 0x7fefc1e08a70 [0x7fff8b4988c0]>{value = 2}
}
2 => "005q0000006OBzS"
3 => "00Dq0000000EAiCEAW"
4 => {
"$classes" => [
0 => "SFUserAccountIdentity"
1 => "NSObject"
]
"$classname" => "SFUserAccountIdentity"
}
]
"$top" => {
"LastUserIdentity" => <CFKeyedArchiverUID 0x7fefc1e089b0 [0x7fff8b4988c0]>{value = 1}
}
"$version" => 100000
}
This data is much better for us to review for sensitive information.
As the name NSKeyedArchiver implies, the data is listed as a key value pair. For instance, the data below is an entry for the "orgIdKey".
"orgIdKey" => <CFKeyedArchiverUID 0x7fefc1e08a90 [0x7fff8b4988c0]>{value = 3}
It is linked to the value listed as "3" in the data block. We can see that the value of 3 is:
3 => \"00Dq0000000EAiCEAW\"
So, in this case the orgIdKey is 00Dq0000000EAiCEAW
.
Decoding this data can quickly become tedious, but it will frequently reveal username's, passwords, tokens, keys, etc.
SQLite Databases
Check the Data directory for all SQLite databases. Try to open every one of them and review the information. Many times, Cache files with HTTP request/response are stored in these. Session ID's and/or credentials could also be here:
# find $APP_DIR -type f -exec grep -ali sqlite {} \;
# find $APP_DIR -type f -exec grep -ali data {} \;
# find $DATA_DIR -type f -exec grep -ali sqlite {} \;
# find $DATA_DIR -type f -exec grep -ali data {} \;
To open an SQLite database for review:
# sqlite3 filename.db
To list the tables in the database:
> .tables
To review the data in a table:
> select * from tablename;
To exit the database:
> .exit
Recover Deleted Database Records
It is also a good idea to check the SQLite database for "deleted" records. SQLite is configured to simply mark data as deleted, without actually removing it from the database file. Using some basic forensics tools, you can "recover" these records and review them. The main tool is the SQLite Parser.
% python sqlparse.py -f filename.db -r -o report.txt
Review the report.txt file for deleted/recovered records.
SQLCipher Databases
SQLCipher is an open-source library that provides transparent, secure 256-bit AES encryption for SQLite databases. Essentially, it is a specialized build of SQLite to perform on-the-fly database encryption. It uses the standard SQLite APIs to manipulate tables using SQL.
To interact with a SQLCipher encrypted database, you will need the "sqlcipher" command, or an SQLite utility capable of interacting with the encrypted database.
Note: To migrate from SQLCipher 3.x to 4.x, you will need to run these commands in the sqlcipher session:
PRAGMA key = '<key material>';
PRAGMA cipher_migrate;