Skip to content

iOS

iOS Platform

Important Folders

Applications: /private/var/containers/Bundle/Application
System Applications:
Application Data Directories: /private/var/mobile/Containers/Data/Application/
Install Programs: /usr/local/sbin
Keyboard Cache: /private/var/mobile/Library/Keyboard/dynamic-text.dat
Screenshot Directory: /private/var/mobile/Media/DCIM/100APPLE

Get data from application sandbox directory

Push notifications

TODO
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app

Firebase

Check Firebase settings by finding the https://\<firebaseProjectName\>.firebaseio.com/.json request

Crypto

New Crypto should use the Swift Crypto Library.

Apple CryptoKit- New for iOS 13

Things to Grep for:
- CommonCryptor
- CCCrypt
- CCCryptorCreate
- SecRandomCopyBytes

T2 Chip

Official Apple Documentation on the T2 Chip

Contains Device specific AES 256-bit hardware key that only the crypto engine can access. This is used to encrypt all of the data on the device.

Creating a key in the secure enclave:*

// private key parameters
let privateKeyParams = [
    kSecAttrLabel as String: "privateLabel",
    kSecAttrIsPermanent as String: true,
    kSecAttrApplicationTag as String: "applicationTag",
] as CFDictionary

// public key parameters
let publicKeyParams = [
    kSecAttrLabel as String: "publicLabel",
    kSecAttrIsPermanent as String: false,
    kSecAttrApplicationTag as String: "applicationTag",
] as CFDictionary

// global parameters
let parameters = [
    kSecAttrKeyType as String: kSecAttrKeyTypeEC,
    kSecAttrKeySizeInBits as String: 256,
    kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
    kSecPublicKeyAttrs as String: publicKeyParams,
    kSecPrivateKeyAttrs as String: privateKeyParams,
] as CFDictionary

var pubKey, privKey: SecKey?
let status = SecKeyGeneratePair(parameters, &pubKey, &privKey)

if status != errSecSuccess {
    // Keys created successfully
}

File Data Protections

  • Every file on the file system can have a different iOS File Protection set.
  • This encryption key is derived from the UID and the PBKDF2 of the passcode.
  • Each file has a different key that is stored in the File Metadata

Listing the files and their corresponding File Protections in Objection:


Listing the files and their corresponding File Protections in Frida:

frida -U -f com.tinyspeck.chatlyio --no-pause -l /opt/Memory/Mobile/frida_iOS_helper_functions.js -e "getDataProtectionKeysForAllPaths()"
     ____
    / _  |   Frida 12.8.7 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://www.frida.re/docs/home/
Spawning `com.tinyspeck.chatlyio`...                                    
Spawned `com.tinyspeck.chatlyio`. Resuming main thread!                 
[iOS Device::com.tinyspeck.chatlyio]-> getDataProtectionKeysForAllPaths()
[
    {
        "fileProtectionKey": "NSFileProtectionCompleteUntilFirstUserAuthentication",
        "path": "/private/var/mobile/Containers/Data/Application/2E1B100C-CD63-4241-AAF9-58E6C4DFC09C/StoreKit/receipt"
    },
    {
        "fileProtectionKey": "NSFileProtectionNone",
        "path": "/private/var/mobile/Containers/Data/Application/2E1B100C-CD63-4241-AAF9-58E6C4DFC09C/.com.apple.mobile_container_manager.metadata.plist"
    },
    {
        "fileProtectionKey": "NSFileProtectionCompleteUntilFirstUserAuthentication",
        "path": "/private/var/mobile/Containers/Data/Application/2E1B100C-CD63-4241-AAF9-58E6C4DFC09C/Library/Caches/com.hackemist.SDImageCache/default/e062188527582103ec63e729fc92c3af.png"
    },
    {
        "fileProtectionKey": "NSFileProtectionCompleteUntilFirstUserAuthentication",
        "path": "/private/var/mobile/Containers/Data/Application/2E1B100C-CD63-4241-AAF9-58E6C4DFC09C/Library/Caches/Snapshots/com.tinyspeck.chatlyio/[email protected]"
    },
[...]

NSFileProtectionComplete: The class key is protected with a key derived from the user passcode and the device UID. Shortly after the user locks a device (10 seconds, if the 'Require Password' setting is set to 'Immediately'), the decrypted class key is discarded, rendering all data in this class inaccessible until the user enters the passcode again.

NSFileProtectionCompleteUntilFirstUserAuthentication: This class behaves in the same way as the 'NSFileProtectionComplete' attribute, except that the decrypted class key is not removed from memory when the device is locked. The protection in this class has similar properties to desktop full-disk encryption, and protects data from attacks that involve a reboot.

NSFileProtectionCompleteUnlessOpen: Some files may need to be written while the device is locked. A good example of this is a mail attachment downloading in the background. This behavior is achieved by using asymmetric elliptic curve cryptography. The ephemeral public key for the agreement is stored alongside the wrapped per-file key. As soon as the file is closed, the per-file key is wiped from memory. To open the file again, the shared secret is recreated using the Protected Unless Open class's private key and the file's ephemeral public key, which are used to unwrap the per-file key that is then used to decrypt the file.

NSFileProtectionNone: This class key is only protected by device level encryption. Since all the keys needed to decrypt files in this class are stored on the device, the encryption only affords the benefit of fast remote wipe.

Keychain

  • Data is stored in a SQLLite Database but can only be accessed through the Keychain API.
  • Can be accessed through the API with the SecItemAdd, SecItemUpdate, SecItemCopyMatching, SecItemDelete

Accessibility Values:
- kSecAttrAccessibleAlways: The data in the Keychain item can always be accessed, regardless of whether the device is locked.
- kSecAttrAccessibleAlwaysThisDeviceOnly: The data in the Keychain item can always be accessed, regardless of whether the device is locked. The data won't be included in an iCloud or local backup.
- kSecAttrAccessibleAfterFirstUnlock: The data in the Keychain item can't be accessed after a restart until the device has been unlocked once by the user.
- kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly: The data in the Keychain item can't be accessed after a restart until the device has been unlocked once by the user. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
- kSecAttrAccessibleWhenUnlocked: The data in the Keychain item can be accessed only while the device is unlocked by the user.
- kSecAttrAccessibleWhenUnlockedThisDeviceOnly: The data in the Keychain item can be accessed only while the device is unlocked by the user. The data won't be included in an iCloud or local backup.
- kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly: The data in the Keychain can be accessed only when the device is unlocked. This protection class is only available if a passcode is set on the device. The data won't be included in an iCloud or local backup.

Control Flags:
- kSecAccessControlDevicePasscode: Access the item via a passcode.
- kSecAccessControlBiometryAny: Access the item via one of the fingerprints registered to Touch ID. Adding or removing a fingerprint won't invalidate the item. Data with this flag are stored in the Secure Enclave itself
- kSecAccessControlBiometryCurrentSet: Access the item via one of the fingerprints registered to Touch ID. Adding or removing a fingerprint will invalidate the item. Data with this flag are stored in the Secure Enclave itself
- kSecAccessControlUserPresence: Access the item via either one of the registered fingerprints (using Touch ID) or default to the passcode.

Dump all keychain data using keychain_dumper.

Note

Keychain Data is not wiped by default when an app is removed

iOS Versions

iOS 11 Changes

Certificates in iOS 11 & 12

Root Certs have to be installed then manually trusted.

Settings -> General -> About -> Certificate Trust Settings

iOS Sandbox

The App Sandbox restricts apps to specific containers.

Apps are prevented from executing mmap and mmprotect to making pages executable if they are writable.

Sandblaster is a tool for reversing (decompiling) binary Apple sandbox profiles.

SSH

Through Network:

ssh -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null [email protected]

Through USB:

python2 /opt/iOS/needle/needle/libs/usbmuxd/tcprelay.py -t 22:2222
ssh -p 2222 -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null [email protected]

Files

Binary Cookie Files are stored in the Application directory and can contain sensitive cookies.

Use BinaryCookieReader.py to read these files.

Example:

python BinaryCookieReader.py  Cookies.binarycookies

PList Files

Plist files can either be a XML data or a Binary equivalent of xml data.

Example:

file some_file.plist
plist some_file.plist

SQLite

Example:


SQLite cipher

Realm Databases

https://realm.io/docs/objc/latest/
https://realm.io/docs/swift/latest/

How to encrypt databases:

// Open the encrypted Realm file where getKey() is a method to obtain a key from the Keychain or a server
let config = Realm.Configuration(encryptionKey: getKey())
do {
  let realm = try Realm(configuration: config)
  // Use the Realm as normal
} catch let error as NSError {
  // If the encryption key is wrong, `error` will say that it's an invalid database
  fatalError("Error opening realm: \(error)")
}

Couchbase Lite Databases

https://github.com/couchbase/couchbase-lite-ios

YapDatabase

https://github.com/yapstudios/YapDatabase

Dump Process Memory

LLDB:
1. Pull an iOS-compatible version of the debugserver binary (requires Xcode and command-line tool installation)

hdiutil attach /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/<LATESTiOSVERSION>/DeveloperDiskImage.dmg
cp /Volumes/DeveloperDiskImage/usr/bin/debugserver .

2. Push debugserver binary to device
3. chmod +x the binary on the device
4. Run ./debugserver *:1234 -a <TARGETAPP> on the iOS device
5. Run lldb on your testing machine
6. On LLDB run the commands
process connect connect://<DEVICEIP>:1234
process save-core <OUTPUTFILENAME>

Frida

  1. Download the fridump tool (a bit outdated, but still works) from this link
    mkdir memorydumpout
    python fridump.py -o memorydumpout -u <TARGETPROCESSNAME>
    

Unique ID

UDID is not accessible through the API since iOS 5. This is because it is used in other apple requests to identify the device.

identifierForVendor: A UUID that is the same for each application certificate on the same device. This ID can change when every app that is signed by the same key is removed from the device.

advertisingIdentifier: The same ID for every app on the device. This can be changed through a Reset All Content and Settings or a Reset Advertising Identifier.

Source

Unique Device Identifier

  • has not been used as an Identifier since iOS 5
  • You can get this identifier from itunes

Get UDID from ioreg:

$ ioreg -p IOUSB -l | grep "USB Serial"
  |         "USB Serial Number" = "9e8ada44246cee813e2f8c1407520bf2f84849ec"

Get UDID from idevice Installer:

$ brew install ideviceinstaller
$ idevice_id -l
316f01bd160932d2bf2f95f1f142bc29b1c62dbc

Get UDID from system_profiler:

system_profiler SPUSBDataType | sed -n -e '/iPad/,/Serial/p;/iPhone/,/Serial/p;/iPod/,/Serial/p' | grep "Serial Number:"
  2019-09-08 10:18:03.920 system_profiler[13251:1050356] SPUSBDevice: IOCreatePlugInInterfaceForService failed 0xe00002be
              Serial Number: 64655621de6ef5e56a874d63f1e1bdd14f7103b1

Get UDID from instruments:

instruments -s devices

Networking

Redirect traffic

Though /etc/hosts

127.0.0.1       localhost
::1             localhost
255.255.255.255 broadcasthost
192.168.1.6     example.com

Non-Jailbroken TCPdump

rvictl -s <UDID>
Starting device <UDID> [SUCCEEDED] with interface rvi0

Setting up Full Proxy of TCP

TODO:

Burp traffic through USB

Port Forward:

ssh -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -R 8080:localhost:8080 root@localhost -p 2222

Certificate Pinning

Using Standard Apple:
- connection: canAuthenticateAgainstProtectionSpace:
- connection: forAuthenticationChallenge

TrustKit:
AlamoFire:
- ServerTrustPolicy
AFNetworking:
- AFSecurityPolicy

Things to Grep For:
- NSURLSession
- CFStream
- AFNetworking

Inter Process Communication (IPC)

XPC Services

  • Is managed by launchd to send information from one process to another.
  • Uses the NSXPCConnection and XPC Services APIs

Classes to grep for:
- NSXPCConnection
- NSXPCInterface
- NSXPCListener
- NSXPCListenerEndpoint

Security Attributes:
- https://www.objc.io/issues/14-mac/xpc/#security-attributes-of-the-connection

Mach Ports

  • Kernel level IPC messages

Things to grep for:
- mach_port_t
- mach_msg_*
- CFMachPort
- CFMessagePort
- NSMachPort
- NSMessagePort

NSFileCoordinator

  • Used to share files with another app.
  • Example Sharing photos with another app.

Things to grep for:
- NSFileCoordinator

URL Schema

URLs are contained in the Entitlements file.
- application:continueUserActivity:restorationHandler:
- openURL:options:completionHandler:
- application:openURL:options:
- application:will-FinishLaunchingWithOptions:
- application:didFinishLaunchingWithOptions:
- application:handleOpenURL:
- application:openURL:sourceApplication:annotation:

In Info.plist
- LSApplicationQueriesSchemes

Open URL with Frida:

function openURL(url) {
  var UIApplication = ObjC.classes.UIApplication.sharedApplication();
  var toOpen = ObjC.classes.NSURL.URLWithString_(url);
  return UIApplication.openURL_(toOpen);
}

openURL("tel://234234234")

UIActivity Sharing

  • Sharing data though specific mechanism like airdrop

Things to grep for:
- initWithActivityItems:applicationActivities:
- application:openURL:options:
- application:openURL:sourceApplication:annotation:
- excludedActivityTypes

App Extensions

These are located in the applicaiton IPA /var/containers/Bundle/Application/15E6A58F-1CA7-44A4-A9E0-6CA85B65FA35/ Telegram X.app/PlugIns directory

[NSExtensionContext openURL:completionHandler:]
application:shouldAllowExtensionPointIdentifier:
NSExtensionContext - inputItems

WebView

Things to Grep for:
- WKWebView/UIWebView
- javaScriptEnabled
- JavaScriptCanOpenWindowsAutomatically
- hasOnlySecureContent
- loadHTMLString:baseURL:
- loadData:MIMEType:textEncodingName:baseURL:
- pathForResource:ofType:
- URLForResource:withExtension:
- loadRequest:
- loadFileURL:allowingReadAccessToURL:
- SFSafariViewController
- WKScriptMessageHandler

Log Files

Read Log Files on Device:

iPhone:~ root# socat - UNIX-CONNECT:/var/run/lockdown/syslog.sock

Reading Log Files from Computer:

idevicesyslog > app_name_$(date +%Y-%m-%d).log

WhiteBox

Check for these functions
- NSLog
- NSAssert
- NSCAssert
- fprintf

Backup

You can use the NSURLIsExcludedFromBackupKey and CFURLIsExcludedFromBackupKey file system properties to exclude files and directories from backups.

ObjC Example:

- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *) filePathString
{
    NSURL* URL= [NSURL fileURLWithPath: filePathString];
    assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);

    NSError *error = nil;
    BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
                                  forKey: NSURLIsExcludedFromBackupKey error: &error];
    if(!success){
        NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
    }
    return success;
}

Swift Example:

enum ExcludeFileError: Error {
    case fileDoesNotExist
    case error(String)
}

func excludeFileFromBackup(filePath: URL) -> Result<Bool, ExcludeFileError> {
    var file = filePath

    do {
        if FileManager.default.fileExists(atPath: file.path) {
            var res = URLResourceValues()
            res.isExcludedFromBackup = true
            try file.setResourceValues(res)
            return .success(true)

        } else {
            return .failure(.fileDoesNotExist)
        }
    } catch {
        return .failure(.error("Error excluding \(file.lastPathComponent) from backup \(error)"))
    }
}

Backing Up the iPhone

  • Setting up password is not Mandatory????

Setting a Backup Password (Optional):

>>> idevicebackup2 -i encryption on
Started "com.apple.mobilebackup2" service on port 49439.
Negotiated Protocol Version 2.1
Enter new backup password: *********
Enter new backup password (repeat): *********
Backup encryption has been enabled successfully.

Backing up the Device:

idevicebackup2 -u 70a58077f305c2a6e64f5eb74660a8619cfda34e --debug -i backup --full .
Backup directory is "."

Clipboard

iOS < 9 malicious apps can retrieve clipboard information when backgrounded using [UIPasteboard generalPasteboard].string. iOS >= 9 can only retrieve this information when in the foreground.

There are two types of Pasteboards. A System and a TeamID pasteboard.

Things to grep for:
- generalPasteboard
- pasteboardWithName:create:
- pasteboardWithUniqueName
- removePasteboardWithName:
- setItems:options:
- UIPasteboardOptionLocalOnly
- UIPasteboardOptionExpirationDate
- setPersistent:

Keyboard

By default iOS caches keyboard data and stores it in /private/var/mobile/Library/Keyboard/dynamic-text.dat.

This can be disabled using the textObject.autocorrectionType = UITextAutocorrectionTypeNo; or the textObject.secureTextEntry = YES; flag.

Non Jailbroken Devices

Go to another app, see enter a partial match to some sensitive data and if a completion is offered.

Screenshots

iOS takes a screenshot when the application is backgrounded so it can show its self on the application switching screen. For sensitive screens the application should put a screen overlay to prevent sensitive information from leaking to another application.

The screenshots are located in the /var/mobile/Containers/Data/Application/$APP_ID/Library/Caches/Snapshots/ folder.

Screen Overlay Code:

@property (UIImageView *)backgroundImage;

- (void)applicationDidEnterBackground:(UIApplication *)application {
    UIImageView *myBanner = [[UIImageView alloc] initWithImage:@"overlayImage.png"];
    self.backgroundImage = myBanner;
    [self.window addSubview:myBanner];
}