Skip to main content

App Configuration / Hard-Coded data / IPA File

An iOS app can be configured in many different places. Typically, these are kept in Property List (PLIST), SQLite Databases, JSON, or XML files.

First, extract the IPA file and dump the headers from the executable:

unzip -qq -d App ExampleApp.ipa
ktool dump --headers --out App/headers App/Payload/Example.app/${BINARY}

To get the application binary:

/usr/libexec/PlistBuddy -c Print:CFBundleExecutable App/Payload/Example.app/Info.plist

Note: This will extract the contents of the IPA into the App directory. This path is used throughout these documents. If you change your path to something else, remember to use that path instead of App.

Review Info.plist

The Info.plist file is the main application configuration. It contains things like permissions, URL schemes, applicable devices, etc.

To review the Info.plist file on a macOS system:

cd App/Payload/Example.app
plutil -p Info.plist

Apple documentation for iOS Info.plist Keys:

https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/uid/TP40009252-SW1

Check for Other PLIST Files

To search for other PLIST files on the device:

find App -type f -exec grep -ali plist {} \;

It is not uncommon to find a PLIST file embedded in a PLIST file. This is typically done using the NSKeyedArchiver APIs for serialization of data. It will look like a bunch of hex strings, because that is exactly what it is. See the next section to decode this information.

Decode NSKeyedArchiver Data

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_00Dq0000000EAiCEAW?
Z$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.

Search for Databases

Typically, mobile databases are dynamically created when the application first launches. The only thing you can get from the IPA file is if the app uses a particular type of database, based on the Libraries or Header files.

To search for database usage:

grep -riIE 'sqlite|sqlite3|sqlcipher|couchbase|realm' App

Search for JSON/XML/Text/Certificates/Other Files

To search for these files:

find App -iname \*.txt
find App -iname \*.xml
find App -iname \*.json
find App -iname \*.cer
find App -iname \*.pem
find App -iname \*.cert
find App -iname \*.crt
find App -iname \*.pub
find App -iname \*.key
find App -iname \*.pfx
find App -iname \*.p12
find App -iname \*.pkcs7
find App -iname \*.html
find App -iname \*.md
find App -iname \*.js
find App -iname \*.m
find App -iname \*.swift
find App -iname \*.log

Jailbreak Detection

grep -iwE 'cydia|jail|bash|sshd|apt|isJailbroken|frida|sileo|substrate|jb|substrate|apt' App

Hardcoded Sensitive Information

Caution: This may provide false positives!

/usr/bin/grep -rwiE 'password\s*=\s*|pass\s*=\s*|username\s*=\s*|secret\s*=\s*|key\s*=\s*|token\s*=\s*' App
/usr/bin/grep -rE 'PRIVATE KEY|ssh-' App
/usr/bin/grep -riE '(Head\w*: Auth\w*:)' App
/usr/bin/grep -riE '(?i)stripe(.{0,20})?[sr]k_live_[0-9a-zA-Z]{24}' App

Biometrics Usage

grep -riIhE 'LAContext' App

Pasteboard Usage

Check if the app monitors the clipboard:

grep -riIEh 'UIPasteboardChangedNotification|generalPasteboard\]\.string|@select(cut\:)|@select(copy\:)' App/headers

Firebase Usage

Check if the app utilizes Google Firebase for backend database:

find App -iname GoogleService-Info.plist

If this PLIST file is found, extract the Database URL:

/usr/libexec/PlistBuddy -c 'Print :DATABASE_URL' App/Payload/Example.app/GoogleService-Info.plist

If the app uses Firebase, then check that proper access controls are in place:

curl -o /dev/null --silent --head --write-out '%{http_code}\n' DBURL.json

Note: Change DBURL to the URL found in the PLIST file!

If the HTTP response code is anything other than 200, then proper access controls are in place. Otherwise, you should get a JSON output.