IOS App Sec Pentest


How to use Needle:

Setup devices:


You need to install 2 packages from Cydia: OpenSSH and OpenSSL

!!! You might need to restart your phone if ssh does not work.

Data Forensic

Data structure

Understand the structure of IOS data storage. First you should run this command to track where its related data located.

$ ipainstaller -l // this will return installed applications

$ ipainstaller -i <app-name>

Bundle: /private/var/containers/Bundle/Application/7A8E0B8B-AD6E-4FB6-8386-6CC53CB7281D Application: /private/var/containers/Bundle/Application/7A8E0B8B-AD6E-4FB6-8386-6CC53CB7281D/ Data: /private/var/mobile/Containers/Data/Application/C5EA4ABF-BBF2-43C4-839E-805E7D35C753asd

Important file system locations are:


    • This app's bundle contains the app and all its resources.

    • This directory is visible to users, but users can't write to it.

    • Content in this directory is not backed up.

  • Documents/

    • Use this directory to store user-generated content.

    • Visible to users and users can write to it.

    • Content in this directory is backed up.

    • The app can disable paths by setting NSURLIsExcludedFromBackupKey.

  • Library/

    • This is the top-level directory for all files that aren't user data files.

    • iOS apps usually use the Application Support and Caches subdirectories, but you can create custom subdirectories.

  • Library/Caches/

    • Contains semi-persistent cached files.

    • Invisible to users and users can't write to it.

    • Content in this directory is not backed up.

    • The OS may delete this directory's files automatically when the app is not running and storage space is running low.

  • Library/Application Support/

    • Contains persistent files necessary for running the app.

    • Invisible to users and users can't write to it.

    • Content in this directory is backed up.

    • The app can disable paths by setting NSURLIsExcludedFromBackupKey

  • Library/Preferences/

    • Used for storing properties, objects that can persist even after an application is restarted.

    • Information is saved, unencrypted, inside the application sandbox in a plist file called [BUNDLE_ID].plist.

    • All the key/value pairs stored using NSUserDefaults can be found in this file.

  • tmp/

    • Use this directory to write temporary files that need not persist between app launches.

    • Contains non-persistent cached files.

    • Invisible to users.

    • Content in this directory is not backed up.

    • The OS may delete this directory's files automatically when the app is not running and storage space is running low.


This is normally done by using the application to generate sample data that may be stored in the Keychain, uninstalling the application, then reinstalling the application to see whether the data was retained between application installation Location: /Users/it/Library/Developer/CoreSimulator/Devices/F40812C8-3670-4E88-9A7D-945C40FF2076/data/Library/Keychains To view db: DB Browser for SQLite

Using Needle

Reading the Keychain

To use Needle to read the Keychain, execute the following command:

[needle] > use storage/data/keychain_dump
[needle][keychain_dump] > run

Searching for Binary Cookies

iOS applications often store binary cookie files in the application sandbox. Cookies are binary files containing cookie data for application WebViews. You can use Needle to convert these files to a readable format and inspect the data. Use the following Needle module, which searches for binary cookie files stored in the application container, lists their data protection values, and gives the user the options to inspect or download the file:

[needle] > use storage/data/files_binarycookies
[needle][files_binarycookies] > run

Searching for Property List Files

iOS applications often store data in property list (plist) files that are stored in both the application sandbox and the IPA package. Sometimes these files contain sensitive information, such as usernames and passwords; therefore, the contents of these files should be inspected during iOS assessments. Use the following Needle module, which searches for plist files stored in the application container, lists their data protection values, and gives the user the options to inspect or download the file:

[needle] > use storage/data/files_plist
[needle][files_plist] > run

Searching for Cache Databases

iOS applications can store data in cache databases. These databases contain data such as web requests and responses. Sometimes the data is sensitive. Use the following Needle module, which searches for cache files stored in the application container, lists their data protection values, and gives the user the options to inspect or download the file:

[needle] > use storage/data/files_cachedb
[needle][files_cachedb] > run

Searching for SQLite Databases

iOS applications typically use SQLite databases to store data required by the application. Testers should check the data protection values of these files and their contents for sensitive data. Use the following Needle module, which searches for SQLite databases stored in the application container, lists their data protection values, and gives the user the options to inspect or download the file:

[needle] > use storage/data/files_sql
[needle][files_sql] > run

Keyboard Cache

Several options for simplifying keyboard input are available to users. These options include autocorrection and spell checking. Most keyboard input is cached by default, in /private/var/mobile/Library/Keyboard/dynamic-text.dat

Static Analysis

  • Search through the source code for similar implementations, such as

textObject.autocorrectionType = UITextAutocorrectionTypeNo;
textObject.secureTextEntry = YES;
  • Open xib and storyboard files in the Interface Builder of Xcode and verify the states of Secure Text Entry and Correction in the Attributes Inspector for the appropriate object.

The application must prevent the caching of sensitive information entered into text fields. You can prevent caching by disabling it programmatically, using the textObject.autocorrectionType = UITextAutocorrectionTypeNo directive in the desired UITextFields, UITextViews, and UISearchBars. For data that should be masked, such as PINs and passwords, set textObject.secureTextEntry to "YES."

UITextField *textField = [ [ UITextField alloc ] initWithFrame: frame ];
textField.autocorrectionType = UITextAutocorrectionTypeNo;

Dynamic Analysis

If a jailbroken iPhone is available, execute the following steps:

  1. Reset your iOS device keyboard cache by navigating to Settings > General > Reset > Reset Keyboard Dictionary.

  2. Use the application and identify the functionalities that allow users to enter sensitive data.

  3. Dump the keyboard cache file dynamic-text.dat into the following directory (which might be different for iOS versions before 8.0): /private/var/mobile/Library/Keyboard/

  4. Look for sensitive data, such as username, passwords, email addresses, and credit card numbers. If the sensitive data can be obtained via the keyboard cache file, the app fails this test.

Or use Needle:

[needle] > use storage/caching/keyboard_autocomplete
[needle][files_sql] > run


After the app data has been backed up, review the data that's in the backed up files and folders. The following directories should be reviewed for sensitive data:

  • Documents/

  • Library/Application Support/

  • Library/Preferences/

Or with Needle:

[needle] > use storage/backup/icloud_content_frida
[needle][files_sql] > run



Memory Dump

➜ fridump git:(master) ✗ frida-ps -U
PID Name
---- ------
1026 Gadget
➜ fridump git:(master) python3 -u Gadget -s
______ _ _
| ___| (_) | |
| |_ _ __ _ __| |_ _ _ __ ___ _ __
| _| '__| |/ _` | | | | '_ ` _ \| '_ \
| | | | | | (_| | |_| | | | | | | |_) |
\_| |_| |_|\__,_|\__,_|_| |_| |_| .__/
| |
Current Directory: /Users/foo/PentestTools/iOS/fridump
Output directory is set to: /Users/foo/PentestTools/iOS/fridump/dump
Creating directory...
Starting Memory dump...
Progress: [##################################################] 100.0% Complete
Running strings on all files:
Progress: [##################################################] 100.0% Complete
Finished! Press Ctrl+C


Touch ID

$ use ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"]

If you're using Needle, run the "hooking/frida/script_touch-id-bypass" module and follow the prompts. This will spawn the application and instrument the evaluatePolicy function. When prompted to authenticate via Touch ID, tap cancel. If the application flow continues, then you have successfully bypassed Touch ID. A similar module (hooking/cycript/cycript_touchid) that uses cycript instead of frida is also available in Needle.Once you're strong enough, save the world:

What if it can bypass, what was the reason? I've been searching for it and here are my analysis & findings.

First, look at the module hooking/frida/script_touch-id-bypass we can see the payload with variable JS

JS = ''' if(ObjC.available) {
var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"];
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
send("Hooking Touch Id..")
var block = new ObjC.Block(args[4]);
const appCallback = block.implementation;
block.implementation = function (error, value) {
const result = appCallback(1, null); return result; }; }, }); }
else { console.log("Objective-C Runtime is not available!"); } '''

The variable JS we can consider is a payload which will be hooked to bypass the touch ID process. As far as I know that the LocalAuthentication.framework or the Security.framework, it does only return a boolean and no data to proceed with. So what if we can change this boolean value to True?. Bingo! we got it through ?!

Quick search around for ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"] as it's a part of the payload JS. I finally found out that "As you now know, the LAContext class specifically does local authentication. It does not verify anything externally or anything like that. It simply relies on iOS to present the relevant dialogs and confirm authentication.

Methods with which you confirm local authentication include the devices passcode, passphrase or via TouchID. An iOS application can not confirm the devices passphrase / TouchID directly via code, but Apple made it possible via the LAContext helper class. This means that an iOS application can not control the authentication dialogs themselves, but can make them pop up.

From a code perspective, once you have set up a new LAContext instance, the evaluatePolicymethod is called, giving iOS a chance to present the relevant dialogs and perform the authentication. Depending on the success or failure of the authentication itself, a reply block is invoked that includes a boolean indicating if it was successful or not. The code block itself that gets executed is again developer controlled, and usually, based on the value of the success boolean, the application would continue accordingly or present an authentication failed error." - explained by Leon Jacobs

And here is the main reason: Within objection, when you run the ios ui touchid_bypass command, a hook is executed that listens for invocations of the -[LAContext evaluatePolicy:localizedReason:reply:] selector. If the evaluatePolicy method is called, the hook will replace the success boolean to a True in the code block that is executed when a reply is received.

By OWASP MSTG recommendation:

It is important to remember that Local Authentication framework is an event-based procedure and as such, should not the sole method of authentication. Though this type of authentication is effective on the user-interface level, it is easily bypassed through patching or instrumentation.

  • Verify that sensitive processes, such as re-authenticating a user triggering a payment transaction, are protected using the Keychain services method.

  • Verify that the kSecAccessControlUserPresence policy and kSecAttrAccessibleWhenPasscodeSetThisDeviceOnlyprotection classes are set when the SecAccessControlCreateWithFlags method is called.



App Transport Security (ATS) is a set of security checks that the operating system enforces when making connections with NSURLConnection, NSURLSession and CFURL to public hostnames. ATS is enabled by default for applications build on iOS SDK 9 and above.

ATS is enforced only when making connections to public hostnames. Therefore any connection made to an IP address, unqualified domain names or TLD of .local is not protected with ATS.

The following is a summarized list of App Transport Security Requirements:

  • No HTTP connections are allowed

  • The X.509 Certificate has a SHA256 fingerprint and must be signed with at least a 2048-bit RSA key or a 256-bit Elliptic-Curve Cryptography (ECC) key.

  • Transport Layer Security (TLS) version must be 1.2 or above and must support Perfect Forward Secrecy (PFS) through Elliptic Curve Diffie-Hellman Ephemeral (ECDHE) key exchange and AES-128 or AES-256 symmetric cipher

ATS Exceptions

ATS restrictions can be disabled by configuring exceptions in the Info.plist file under the NSAppTransportSecurity key. These exceptions can be applied to:

  • allow insecure connections (HTTP),

  • lower the minimum TLS version,

  • disable PFS or

  • allow connections to local domains.

ATS exceptions can be applied globally or per domain basis. The application can globally disable ATS, but opt in for individual domains. The following listing from Apple Developer documentation shows the structure of the dictionary.

NSAppTransportSecurity : Dictionary {
NSAllowsArbitraryLoads : Boolean
NSAllowsArbitraryLoadsForMedia : Boolean
NSAllowsArbitraryLoadsInWebContent : Boolean
NSAllowsLocalNetworking : Boolean
NSExceptionDomains : Dictionary {
<domain-name-string> : Dictionary {
NSIncludesSubdomains : Boolean
NSExceptionAllowsInsecureHTTPLoads : Boolean
NSExceptionMinimumTLSVersion : String
NSExceptionRequiresForwardSecrecy : Boolean // Default value is YES
NSRequiresCertificateTransparency : Boolean

The following table summarizes the global ATS exceptions. For more information about these exceptions, please refer to table 2 in the official Apple developer documentation.




Disable ATS restrictions globally excepts for individual domains specified under NSExceptionDomains


Disable ATS restrictions for all the connections made from web views


Allow connection to unqualified domain names and .local domains


Disable all ATS restrictions for media loaded through the AV Foundations framework

The following table summarizes the per-domain ATS exceptions. For more information about these exceptions, please refer to table 3 in the official Apple developer documentation.




Indicates whether ATS exceptions should apply to subdomains of the named domain


Allows HTTP connections to the named domain, but does not affect TLS requirements


Allows connections to servers with TLS versions less than 1.2


Disable perfect forward secrecy (PFS)

Analyzing the ATS Configuration

  1. ipainstaller -i

  2. Choose Bundle: /private/var/containers/Bundle/Application/816A1089-AC51-43F0-8192-B8FA9C4

  3. Go to Bundle directory:cd /private/var/containers/Bundle/Application/816A1089-AC51-43F0-8192-B8FA9C4

  4. You will see the folder <> then: cd <>

  5. Look for Info.plist . It’s a binary encoded file and has to be converted to a human readable format for the analysis.

  6. Use plutil -convert xml1 Info.plist to convert it to XML format.

Now we can start analyzing it. The application may have ATS exceptions defined to allow it’s normal functionality. For an example, the Firefox iOS application has ATS disabled globally. This exception is acceptable because otherwise the application would not be able to connect to any HTTP website that does not have all the ATS requirements.

To understand what is the best practices regarding ATS configuration, you can go here 😁