mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-30 08:30:57 +00:00
368 lines
22 KiB
Markdown
368 lines
22 KiB
Markdown
# iOS Pentesting
|
||
|
||
## Privilege Separation and Sandbox
|
||
|
||
Applications the user can access run as the **mobile** user while critical system processes run as **root**.
|
||
However, the sandbox allows better control over actions that processes and applications can perform.
|
||
|
||
For example, even if two processes run as the same user \(mobile\), they are **not allowed to access or modify each other's data**.
|
||
|
||
Each application is installed under **`private/var/mobile/Applications/{random ID}`**
|
||
Once installed, applications have limited read access to some system areas and functions \(SMS, phone call...\). If an application wants to access a **protected area,** a **pop-up requesting permission** appears.
|
||
|
||
## Jailbreaking
|
||
|
||
Apple strictly requires that the code running on the iPhone must be **signed by a certificate issued by Apple**. **Jailbreaking** is the process of actively **circumventing such restrictions** and other security controls put in places by the OS. Therefore, once the device is jailbroken, the **integrity check** which is responsible for checking apps being installed is patched so it is **bypassed**.
|
||
|
||
{% hint style="info" %}
|
||
Unlike Android, **you cannot switch to "Developer Mode"** in iOS to run unsigned/untrusted code on the device.
|
||
{% endhint %}
|
||
|
||
The most important side effect of Jailbreaking is that it **removes any sandboxing put in place by the OS**. Therefore, any **app on the device can read any file** on the filesystem, including other apps files, cookies and keychain.
|
||
|
||
A jailbroken device allows users to **install unapproved apps** and leverage **more APIs**, which otherwise aren't accessible.
|
||
|
||
There are 2 types of jailbreaks:
|
||
|
||
* **Tethered**: Temporary jailbreak that requires the device to be connected to a computer every-time the device needs a restart. The jailbreak is reversed otherwise.
|
||
* **Untethered**: Rebooting the device does not reset the jailbreak.
|
||
|
||
**For regular users it's not recommended to jailbreak the mobile.
|
||
Note also that updating the OS removes the effect of jailbreaking.**
|
||
|
||
## **Simulator**
|
||
|
||
All the tools required to build and support an iOS app are **only officially supported on Mac OS**.
|
||
Apple's de facto tool for creating/debugging/instrumenting iOS applications is **Xcode**. It can be used to download other components such as **simulators** and different **SDK** **versions** required to build and **test** your app.
|
||
It's highly recommended to **download** Xcode from the **official app store**. Other versions may be carrying malware.
|
||
|
||
The simulator files can be found in `/Users/<username>/Library/Developer/CoreSimulator/Devices`
|
||
|
||
To open the simulator, run Xcode, then press in the _Xcode tab_ --> _Open Developer tools_ --> _Simulator_
|
||
In the following image clicking in "iPod touch \[...\]" you can select other device to test in:
|
||
|
||
![](../.gitbook/assets/image%20%28459%29.png)
|
||
|
||
![](../.gitbook/assets/image%20%28460%29.png)
|
||
|
||
### Applications in the Simulator
|
||
|
||
Inside `/Users/<username>/Library/Developer/CoreSimulator/Devices` you may find all the **installed simulators**. If you want to access the files of an application created inside one of the emulators it might be difficult to know **in which one the app is installed**. A quick way to **find the correct UID** is to execute the app in the simulator and execute:
|
||
|
||
```bash
|
||
xcrun simctl list | grep Booted
|
||
iPhone 8 (BF5DA4F8-6BBE-4EA0-BA16-7E3AFD16C06C) (Booted)
|
||
```
|
||
|
||
Once you know the UID the apps installed within it can be found in `/Users/<username>/Library/Developer/CoreSimulator/Devices/{UID}/data/Containers/Data/Application`
|
||
|
||
However, surprisingly you won't find the application here. You need to access `/Users/<username>/Library/Developer/Xcode/DerivedData/{Application}/Build/Products/Debug-iphonesimulator/`
|
||
|
||
And in this folder you can **find the package of the application.**
|
||
|
||
## Apple Developer Program
|
||
|
||
A **provisioning identity** is a collection of public and private keys that are associated an Apple developer account. In order to **sign apps** you need to pay **99$/year** to register in the **Apple Developer Program** to get your provisioning identity. Without this you won't be able to run applications from the source code in a physical device. Another option to do this is to use a **jailbroken device**.
|
||
|
||
Starting in Xcode 7.2 Apple has provided an option to create a **free iOS development provisioning profile** that allows to write and test your application on a real iPhone. Go to _Xcode_ --> _Preferences_ --> _Accounts_ --> _+_ \(Add new Appli ID you your credentials\) --> _Click on the Apple ID created_ --> _Manage Certificates_ --> _+_ \(Apple Development\) --> _Done_
|
||
Then, in order to run your application in your iPhone you need first to **indicate the iPhone to trust the computer.** Then, you can try to **run the application in the mobile from Xcode,** but and error will appear. So go to _Settings_ --> _General_ --> _Profiles and Device Management_ --> Select the untrusted profile and click "**Trust**".
|
||
|
||
Note that **applications signed by the same signing certificate can share resources on a secure manner, like keychain items**.
|
||
|
||
## Objective-C and Swift Basics
|
||
|
||
**Objecttive-C** has a **dynamic runtime**, so when an Objective-C program is executed in iOS, it calls libraries whose **address are resolved at runtime** by comparing the name of the function sent in the message against a list of all the function names available.
|
||
|
||
At the beginning, only apps created by Apple run the iPhones, so they had **access to everything** as they were **trusted**. However, when Apple **allowed** **third party applications,** Apple just removed the headers files of the powerful functions to "hide" them to developers. However, developers found that "safe" functions needed a few of these undocumented functions and just creating a **custom header file with the names of the undocumented functions, it was possible to invoke this powerful hidden functions.** Actually, Apple, before allowing an app to be published, check if the app calls any of these prohibited functions.
|
||
|
||
Then, Swift appeared. As **Swift is statically bound** \(it doesn't resolve the address of the functions in runtime like Objective-C\), it can be checked more easily the calls a Swift program is going to make via static code analysis.
|
||
|
||
## Obfuscation
|
||
|
||
Unlike an Android Application, the binary of an iOS app **can only be disassembled** and not decompiled.
|
||
When an application is submitted to the app store, Apple first verifies the app conduct and before releasing it to the app-store, **Apple encrypts the binary using** [**FairPlay**](https://developer.apple.com/streaming/fps/). So the binary download from the app store is encrypted complicating ting the reverse-engineering tasks.
|
||
|
||
However, note that there are other **third party software that can be used to obfuscate** the resulting binaries.
|
||
|
||
### Removing App Store Encryption
|
||
|
||
In order to run the encrypted binary, the device needs to decrypt it in memory. Then, it's possible to **dump the decrypted binary from the memory**.
|
||
|
||
First, check if the binary is compiled with the PIE \(Position Independent Code\) flag:
|
||
|
||
```bash
|
||
otool -Vh Original_App #Check the last word of the last line of this code
|
||
Home:
|
||
Mach header
|
||
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
|
||
MH_MAGIC_64 X86_64 ALL 0x00 EXECUTE 47 6080 NOUNDEFS DYLDLINK TWOLEVEL PIE
|
||
```
|
||
|
||
If it's set you can use the script [`change_macho_flags.py`](https://chromium.googlesource.com/chromium/src/+/49.0.2623.110/build/mac/change_mach_o_flags.py) to remove it with python2:
|
||
|
||
```bash
|
||
python change_mach_o_flags.py --no-pie Original_App
|
||
otool -Vh Hello_World
|
||
Hello_World:
|
||
Mach header
|
||
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
|
||
MH_MAGIC ARM V7 0x00 EXECUTE 22 2356 NOUNDEFS DYLDLINK TWOLEVEL MH_NO_HEAP_EXECUTION
|
||
```
|
||
|
||
Now that the PIE flag isn't set, the OS will load the program at a **fixed starting location** every-time. In order to find this **location** you can use:
|
||
|
||
```bash
|
||
otool -l Original_App | grep -A 3 LC_SEGMENT | grep -A 1 __TEXT
|
||
segname __TEXT
|
||
vmaddr 0x00004000
|
||
```
|
||
|
||
Then, it's necessary to extract the the memory range that needs to be dumped:
|
||
|
||
```bash
|
||
otool -l Original_App | grep -A 4 LC_ENCRYPTION_INFO
|
||
cmd LC_ENCRYPTION_INFO
|
||
cmdsize 20
|
||
cryptoff 16384
|
||
cryptsize 17416192
|
||
cryptid 0
|
||
```
|
||
|
||
The value of **`cryptoff`** indicated the starting address of the encrypted content and the **`cryptsize`** indicates the size of the encrypted content.
|
||
|
||
So, the `start address` to dump will be `vmaddr + cryptoff` and the `end address` will be the `start address + cryptsize`
|
||
In this case: `start_address = 0x4000 + 0x4000 = 0x8000` __and `end_address = 0x8000 + 0x109c000 = 0x10a4000`
|
||
|
||
With this information it's just necessary to run the application in the jailbroken device, attach to the process with gdb \(`gdb -p <pid>`\) and dump the memory:
|
||
|
||
```bash
|
||
dump memory dump.bin 0x8000 0x10a4000
|
||
```
|
||
|
||
Congrats! You have decrypted the encrypted section in dump.bin. Now **transfer this dump to your computer and overwrite the encrypted section with the decrypted** one:
|
||
|
||
```bash
|
||
dd bs=1 seek=<starting_address> conv=notrunc if=dump.bin of=Original_App
|
||
```
|
||
|
||
There is one more step to complete. The application is still **indicating** in its metadata that it's **encrypted**, but it **isn't**. Then, when executed, the device will try to decrypt the already decrypted section and it's going to fail.
|
||
However, you can use tools like [**MachOView**](https://sourceforge.net/projects/machoview/) to change this info. Just open the binary and set the **cryptid** to 0:
|
||
|
||
![](../.gitbook/assets/image%20%28458%29.png)
|
||
|
||
### Removing App Store Encryption Automatically
|
||
|
||
You can use tools like [**Clutch**](https://github.com/KJCracks/Clutch) to automatically remove the encryption and an app.
|
||
|
||
## IPA Reversing
|
||
|
||
`.ipa` files are **zipped** **packages**, so you can change the extension to `.zip` and **decompress** them. A **complete** **packaged** app ready to be installed is commonly referred to as a **Bundle**.
|
||
After decompressing them you should see `<NAME>.app` , a zipped archive that contains the rest of the resources.
|
||
|
||
* **Info.plist**: A file that contains some of the application specific configurations.
|
||
* **Assets.car**: Another zipped archive that contains assets \(icons\).
|
||
* \*\*\*\*[**Core Data**](https://developer.apple.com/documentation/coredata): It is used to save your application’s permanent data for offline use, to cache temporary data, and to add undo functionality to your app on a single device. To sync data across multiple devices in a single iCloud account, Core Data automatically mirrors your schema to a CloudKit container.
|
||
* \*\*\*\*[**PkgInfo**](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigApplications.html): The `PkgInfo` file is an alternate way to specify the type and creator codes of your application or bundle.
|
||
* **en.lproj, fr.proj, Base.lproj**: Are the language packs that contains resources for those specific languages, and a default resource in case a language isn' t supported.
|
||
|
||
There are multiple ways to define the UI in an iOS application: _storyboard_, _nib_ or _xib_ files.
|
||
|
||
### Binary
|
||
|
||
Inside the `<application-name>.app` folder you will find a binary file called `<application-name>`. This is the file that will be **executed**. You can perform a basic inspection of the binary with the tool **`otool`**:
|
||
|
||
```bash
|
||
otool -Vh DVIA-v2 #Check some compilation attributes
|
||
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
|
||
MH_MAGIC_64 ARM64 ALL 0x00 EXECUTE 65 7112 NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE
|
||
|
||
otool -L DVIA-v2 #Get third party libraries
|
||
DVIA-v2:
|
||
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.1)
|
||
/usr/lib/libsqlite3.dylib (compatibility version 9.0.0, current version 274.6.0)
|
||
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
|
||
@rpath/Bolts.framework/Bolts (compatibility version 1.0.0, current version 1.0.0)
|
||
[...]
|
||
```
|
||
|
||
#### Check if the app is encrypted
|
||
|
||
See if there is any output for:
|
||
|
||
```bash
|
||
otool -l <app-binary> | grep -A 4 LC_ENCRYPTION_INFO
|
||
```
|
||
|
||
#### Disassembling the binary
|
||
|
||
Disassemble the text section:
|
||
|
||
```bash
|
||
otool -tV DVIA-v2
|
||
DVIA-v2:
|
||
(__TEXT,__text) section
|
||
+[DDLog initialize]:
|
||
0000000100004ab8 sub sp, sp, #0x60
|
||
0000000100004abc stp x29, x30, [sp, #0x50] ; Latency: 6
|
||
0000000100004ac0 add x29, sp, #0x50
|
||
0000000100004ac4 sub x8, x29, #0x10
|
||
0000000100004ac8 mov x9, #0x0
|
||
0000000100004acc adrp x10, 1098 ; 0x10044e000
|
||
0000000100004ad0 add x10, x10, #0x268
|
||
```
|
||
|
||
To print the **Objective-C segment** of the sample application one can use:
|
||
|
||
```bash
|
||
otool -oV DVIA-v2
|
||
DVIA-v2:
|
||
Contents of (__DATA,__objc_classlist) section
|
||
00000001003dd5b8 0x1004423d0 _OBJC_CLASS_$_DDLog
|
||
isa 0x1004423a8 _OBJC_METACLASS_$_DDLog
|
||
superclass 0x0 _OBJC_CLASS_$_NSObject
|
||
cache 0x0 __objc_empty_cache
|
||
vtable 0x0
|
||
data 0x1003de748
|
||
flags 0x80
|
||
instanceStart 8
|
||
```
|
||
|
||
In order to obtain a more compact Objective-C code you can use [**class-dump**](http://stevenygard.com/projects/class-dump/):
|
||
|
||
```bash
|
||
class-dump some-app
|
||
//
|
||
// Generated by class-dump 3.5 (64 bit).
|
||
//
|
||
// class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
|
||
//
|
||
|
||
#pragma mark Named Structures
|
||
|
||
struct CGPoint {
|
||
double _field1;
|
||
double _field2;
|
||
};
|
||
|
||
struct CGRect {
|
||
struct CGPoint _field1;
|
||
struct CGSize _field2;
|
||
};
|
||
|
||
struct CGSize {
|
||
double _field1;
|
||
double _field2;
|
||
};
|
||
```
|
||
|
||
However, the best options to disassemble the binary are: [**Hopper**](https://www.hopperapp.com/download.html?) and [**IDA**](https://www.hex-rays.com/products/ida/support/download_freeware/).
|
||
|
||
## Testing
|
||
|
||
### Storage Access
|
||
|
||
You can use [**iFunBox**](https://www.i-funbox.com/en/page-download.html) to access the all the storage inside an application sandbox/folder
|
||
|
||
{% hint style="info" %}
|
||
Starting in iOS version 8.4, Apple has **restricted the third-party managers to access to the application sandbox**, so tools like iFunbox and iExplorer no longer display/retrieve files from apps installed on the device if the device isn't jailbroken.
|
||
{% endhint %}
|
||
|
||
### Burp Proxy Configuration
|
||
|
||
{% page-ref page="burp-configuration-for-ios.md" %}
|
||
|
||
### Snapshots
|
||
|
||
Whenever you press the home button, iOS **takes a snapshot of the current screen** to be able to do the transition to the application on a much smoother way. However, if **sensitive** **data** is present in the current screen, it will be **saved** in the **image** \(which **persists** **across** **reboots**\). These are the snapshots that you can also access double tapping the home screen to switch between apps.
|
||
|
||
Unless the iPhone is jailbroken, the **attacker** needs to have **access** to the **device** **unblocked** to see these screenshots. By default the last snapshot is stored in the application's sandbox in `/Library/Caches/Snapshots/` folder \(the trusted computers can' t access the filesystem from iOX 7.0\).
|
||
|
||
Once way to prevent this bad behaviour is to put a blank screen or remove the sensitive data before taking the snapshot using the `ApplicationDidEnterBackground()` function.
|
||
|
||
### Keychain
|
||
|
||
A keychain is an **encrypted** **container** where every application can **store** **sensitive** pieces of **information** and only the same app \(or authorised apps\) can retrieve the contents.
|
||
The iOS **generated its own password for the keychain** and **stores** an **encrypted** version of this key in the device. This password is encrypted with AES using an AES key created by a **PBKDF2** function of the **user's passcode + salt** \(the 256 bit device **UID** **only** **accessible** to the secure **enclave chipset** on the device\). Due to the use of this device UID as salt, a device won't be able to decrypt the keychain of a different device even knowing the users passcode.
|
||
|
||
The only ways to try to BF this password is dumping the encrypted key and BF the passcode + salt \(the **pbkdf2** function uses **at least 10000 iteration**s\). Or trying to **BF inside the device** to avoids BFing the salt, however, secure enclave ensures there is at least a **5s delay between 2 failed password attempts**.
|
||
|
||
When a backup process is initiated the keychain **data backed up remains encrypted and the keychain password isn't included in the backup**.
|
||
|
||
**In a jailbroken device the keychain isn't protected.**
|
||
|
||
#### **Attribute types for items saved in the keychain:**
|
||
|
||
* **kSecAttrAccessibleAlways:** These items will not be stored securely in the keychain and are available at all times, even when the device is blocked
|
||
* **kSecAttrAccessibleAfterFirstUnlock**: Items secure in the keychain until the device is first unlocked after a reboot. Then, the items are accessible even when the device is blocked.
|
||
* **kSecAttrAccessibleWhenUnlocked**: Items are secure at rest and when the device is locked. The items are only accessible when the device is unlocked.
|
||
* **kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly**: Like **kSecAttrAccessibleWhenUnlocked** but you a passcode must be set in the device. If the passcode is unset, these secrets are deleted from the device.
|
||
|
||
You can also select the "_**ThisDeviceOnly**_" on keychain objects to ensure that even the encrypted keychains objects **never leave the device during backups**.
|
||
|
||
The iPhone uses the **passcode introduced by the user unlocking the device to decrypt the secrets in the keychain**.
|
||
|
||
iOS uses the _**AppIdentifierPrefix**_ \(Team ID\) and the _**BundleIdentifier**_ \(provided by the dev\) to enforce **access control oven keychain items**. Then, the same team **can** **configure** **2 apps to share keychain items**.
|
||
|
||
Tools like [**Keychain-Dumper**](https://github.com/ptoomey3/Keychain-Dumper) can be used to dump the keychain \(the dive must be jailbroken\).
|
||
|
||
{% hint style="info" %}
|
||
In **iOS earlier than 10.3**, when an application is deleted from the device, iOS **doesn't clean up the keychain**. So on these devices you can **find secrets of deleted apps**.
|
||
{% endhint %}
|
||
|
||
### Cookies
|
||
|
||
iOS store the cookies of the apps in the **`Library/Cookies/cookies.binarycookies`** inside each apps folder. However, developers sometimes decide to save them in the **keychain** as the mentioned **cookie file can be accessed in backups**.
|
||
|
||
To inspect the cookies file you can use [**this python script**](https://github.com/mdegrazia/Safari-Binary-Cookie-Parser).
|
||
|
||
### Plist
|
||
|
||
**plist** files are structured XML files that **contains key-value pairs**. It's a way to store persistent data, so sometimes you may find **sensitive information in these files**. It's recommended to check these files after installing the app and after using intensively it to see if new data is written.
|
||
|
||
The most common way to persist data in plist files is through the usage of **NSUserDefaults**. This plist file is saved inside the app sandbox in **`Library/Preferences/<appBundleID>.plist`**
|
||
|
||
This data cannot be longer accessed directly via a trusted computer, but can be accessed performing a **backup**.
|
||
|
||
### Custom Keyboards
|
||
|
||
From iOS 8.0 Apple allows to install custom extensions for iOS like custom keyboards.
|
||
The installed keyboards can be managed via **Settings** > **General** > **Keyboard** > **Keyboards**
|
||
Custom keyboards can be used to **sniff** the **keystrokes** and send them to the attacker server. However, note that **custom keyboards requiring networking connectivity will be notified to the user.**
|
||
Also, the **user can switch to a different** \(more trusted\) **keyboard** for introducing the credentials.
|
||
|
||
Moreover, **applications can prevent its users from using custom keyboards** within the app \(or at least for sensitive parts of the app\).
|
||
|
||
Note that because of auto-correct and auto-suggestions, the default iOS keyboard will capture and store each non-standard word word in a cache file if the attribute **securetTextEntry** is not set to **true** or if **autoCorrectionType** is not set to **UITextAutoCorrectionTypeNo.**
|
||
|
||
By default the keyboards store this cache inside the applications sandbox in `Library/Keyboard/{locale}-dynamic-text.dat` file. However, it might be saving the date elsewhere.
|
||
It's possible to reset the cache in _**Settings**_ > _**General**_ > _**Reset**_ > _**Reset Keyboard Dictionary**_
|
||
|
||
**Therefore, check always these files and search for possible sensitive information.
|
||
Intercepting the network traffic is another way to check if the custom keyboard is sending keystroked to a remote server.**
|
||
|
||
### **Log Files**
|
||
|
||
The most common ways to debug code is using logging, and the application **may print sensitive information inside the logs**.
|
||
In iOS version 6 and below, logs were world readable \(a malicious app could read logs from other apps and extract sensitive information from there\). **Nowadays, apps can only access their own logs**.
|
||
|
||
However, an **attacker** with **physical** **access** to an **unlocked** device can connect it to a computer and **read the logs** \(note that the logs written to disk by an app aren't removed if the app ins uninstalled\).
|
||
|
||
To inspect the application logs, connect the iPhone to your computer and open _**Xcode**_ > _**Devices**_ > _**{Your device}**_ and you should see the live logs in the console.
|
||
|
||
It's recommended to **navigate through all the screens** of the app and **interact** with **every** UI element and **functionality** of and provide input text in all text fields and **review the logs** looking for **sensitive** **information** exposed.
|
||
|
||
### Clipboard
|
||
|
||
Some applications may save sensitive information inside the clipboard, which is dangerous because then a different application may sniff the clipboard and steal the data.
|
||
|
||
Fortunately, apps signed by the same certificate can create **private** **UIPasteboards**. This way, unlike the global Pasteboard, only **selected** **applications** can share and view the content of the **private** **pasteboard**.
|
||
|
||
Then, it's important to **check that sensitive information isn't being saved inside the global pasteboard**.
|
||
It's also important to check that an **application isn't using the global pasteboard data to perform actions**, as malicious application could tamper this data.
|
||
|
||
An **application can also prevent its users to copy sensitive data to the clipboard** \(which is recommended\).
|
||
|
||
### Custom URI Handlers / Deeplinks / Custom Schemes
|
||
|
||
A custom URI handler can
|
||
|