iOS All The Things - Part IV

Featured image

Agenda of iOS Pentesting:

  1. Intro
  2. iOS Code Security
  3. Third Party Libraries
  4. Inter-Process Communication (IPC)
  5. WebViews Javascript to Native Bridge
  6. Conclusion

Intro

Our journey is nearing its end, In this part, we move beyond the standard techniques to tackle the sophisticated security challenges that define modern iOS applications.

we’re going to explore those advanced areas. We’ll look at how developers try to protect their code from people like us, why the external libraries an app uses can be a major weak spot, how apps communicate with each other (and how we can listen in), and finally, how to map out every single place an attacker could possibly target.

This is where we transition from performing basic tests to conducting comprehensive security assessments that uncover deep, systemic vulnerabilities. Let’s complete our mission.

iOS Code Security

iOS code security encompasses the techniques and mechanisms used to protect an application’s code from unauthorized analysis, reverse engineering, and tampering. For penetration testers, understanding these protections is crucial because they represent the first line of defense we must bypass to assess the application’s true security.

Key Areas of iOS Code Security:

a. Code Obfuscation: make the code difficult for humans to read and understand.

Note: Objective-c is easier to reverse using ghidra.

Example on Obfuscation:

The standard compiler generates binary symbols based on class and function names from the source code. Therefore, if no obfuscation was applied, symbol names remain meaningful and can be easily read straight from the app binary. For instance, a function which detects a jailbreak can be located by searching for relevant keywords (e.g. “jailbreak”). The listing below shows the disassembled function JailbreakDetectionViewController.jailbreakTest4Tapped from the DVIA-v2 app.

  __T07DVIA_v232JailbreakDetectionViewControllerC20jailbreakTest4TappedyypF:
  stp        x22, x21, [sp, #-0x30]! 
  mov        rbp, rsp

After the obfuscation we can observe that the symbol’s name is no longer meaningful as shown on the listing below.

  __T07DVIA_v232zNNtWKQptikYUBNBgfFVMjSkvRdhhnbyyFySbyypF:
  stp        x22, x21, [sp, #-0x30]!
  mov        rbp, rsp

Nevertheless, this only applies to the names of functions, classes and fields. The actual code remains unmodified, so an attacker can still read the disassembled version of the function and try to understand its purpose (e.g. to retrieve the logic of a security algorithm).

SwiftShield: Developers can use third-party tools like Swiftshield to automatically obfuscate their code, making it much harder for attackers to read and understand. This is a commercial product designed specifically to protect iOS applications.

It is now detecting class and method names and is replacing their identifier with an encrypted value.

In the original source code you can see all the class and method identifiers:

image

SwiftShield was now replacing all of them with encrypted values that leave no trace to their original name or intention of the class/method:

image

After executing swiftshield a new directory will be created called swiftshield-output. In this directory another directory is created with a timestamp in the folder name. This directory contains a text file called conversionMap.txt, that maps the encrypted strings to their original values.

  $ cat conversionMap.txt
  //
  // SwiftShield Conversion Map
  // Automatic mode for SwiftSecurity, 2020-01-02 13.51.03
  // Deobfuscate crash logs (or any text file) by running:
  // swiftshield -deobfuscate CRASH_FILE -deobfuscate_map THIS_FILE
  //

  ViewController ===> hTOUoUmUcEZUqhVHRrjrMUnYqbdqWByU
  viewDidLoad ===> DLaNRaFbfmdTDuJCPFXrGhsWhoQyKLnO
  sceneDidBecomeActive ===> SUANAnWpkyaIWlGUqwXitCoQSYeVilGe
  AppDelegate ===> KftEWsJcctNEmGuvwZGPbusIxEFOVcIb
  Deny_Debugger ===> lKEITOpOvLWCFgSCKZdUtpuqiwlvxSjx
  Button_Emulator ===> akcVscrZFdBBYqYrcmhhyXAevNdXOKeG

Hardcoded Secrets

Without proper obfuscation, hardcoded secrets become low-hanging fruit for attackers. When code isn’t obfuscated, sensitive information remains in plain sight within the binary, making it easily discoverable through basic reverse engineering techniques.

In files or Decompiled source code:

Using simple command strings to get sensitive data:

  strings DVIA-v2

  # specify hardcoded secrets we want to search
  strings DVIA-v2 | grep -i "api\|key\|token\|password\|secret"

image

or can search on decompiled source code by ghidra

image

b. Anti-Debugging Protections: Prevent attackers from debugging the application.

c. Anti-Tampering Mechanisms: Detect if the application has been modified.

d. Runtime Protection: Protect the application while it’s running.

e. Debugging Symbols: are crucial metadata generated during the compilation process that map the compiled binary code back to the original source code. They play a significant role in both development and security analysis.

Note: Xcode automatically adds the Get Task Allow entitlement to apps that you build for debugging, while removing the entitlement before App Store submission. This enables Xcode itself to attach to and debug your app during development.

On Linux to check the debugging symbols enabled or not:

  sudo apt-get install llvm
  llvm-objdump --syms DVIA-v2 | grep "      d"

image

  # or can use that
  ipsw ent --input DVIA-v2

image

That output means debugging symbols is enabled.

Testing Methodology

a. Static Analysis:

b. Dynamic Analysis:

c. Bypass Techniques:

Common Vulnerabilities in Code Security

Third Party Libraries

Third-party libraries are pre-built code components that developers integrate into their iOS apps to add functionality without building everything from scratch. While they save development time, they introduce significant security risks that penetration testers must assess.

Here’s what penetration testers need to know:

a. Insecure Dependencies

b. Dylib Risks

Security Concerns:

  # Check for embedded dylibs
  find YourApp.app -name "*.dylib"

  # Check rpath for insecure paths
  strings Binary-App | grep -A 3 LC_RPATH

We can use objection to get dynamic library loads and frameworks:

  ios bundles list_bundles
  ios bundles list_frameworks

image

We can use ipsw to get dynamic library loads and frameworks:

  ipsw macho info DVIA-v2

image

We can use frida script to monitor dylib loading:

  // Real-time interception of library loading
  Interceptor.attach(Module.findExportByName(null, "dlopen"), {
      onEnter: function(args) {
          var path = args[0].readCString();
          console.log("[+] Loading dylib: " + path);
          // Flag suspicious paths outside app bundle
          if (path && !path.includes("/var/containers/Bundle/Application/")) {
              console.log("[!] Suspicious dylib path: " + path);
          }
      }
  });

c. Library Permissions Analysis

Checking Library Permissions:

  # run command on ios jailbroken device
  # Extract entitlements from the app
  ldid -e "Binary-App" > app_entitlements.plist

  # Check embedded framework entitlements
  ldid -e App/Frameworks/Analytics.framework > analytics_entitlements.plist

Common Over-Privileged Libraries:

  <!-- Example of excessive entitlements in library -->
  <key>com.apple.developer.associated-domains</key>
  <array>
      <string>applinks:example.com</string>
      <string>applinks:tracking-library.com</string> <!-- Suspicious -->
  </array>
  <key>com.apple.developer.networking.wifi-info</key> <!-- Often unnecessary -->
  <true/>

Testing Methodology:

Remediation Recommendations

Important Link: special for Dylib

Inter-Process Communication (IPC)

Inter-Process Communication (IPC) allows iOS applications to exchange data and communicate with other apps, extensions, and system services. While IPC enables powerful functionality, it also creates significant security risks that penetration testers must evaluate.

Types of IPC Mechanisms in iOS:

a. URL Schemes:

b. Universal Links:

c. Keychain Sharing:

d. XPC Services:

e. App Extensions:

f. UIActivityViewController:

g. App Groups:

Common IPC Vulnerabilities:

a. URL Scheme Hijacking:

b. Insecure App Group Sharing:

c. Keychain Access Issues:

d. Extension Vulnerabilities:

Security Best Practices

a. URL Scheme Protection:

b. App Group Security:

c. Keychain Hardening:

d. Extension Security:

WebViews Javascript to Native Bridge

The WebView JavaScript to Native bridge allows communication between web content loaded in a WebView and the native iOS application. While powerful for hybrid apps, this bridge introduces significant security risks if not properly implemented.

Types of Webviews:

a. UIWebView: is deprecated starting on iOS 12 and should not be used. Make sure that either WKWebView or SFSafariViewController are used to embed web content. In addition to that, JavaScript cannot be disabled for UIWebView which is another reason to refrain from using it.

b. WKWebView: was introduced with iOS 8 and is the appropriate choice for extending app functionality, controlling displayed content (i.e., prevent the user from navigating to arbitrary URLs) and customizing.

c. SFSafariViewController: is available starting on iOS 9 and should be used to provide a generalized web viewing experience. These WebViews can be easily spotted as they have a characteristic layout which includes the following elements:

Check vulnerable and deprecated Components:

If you have access to the source code, you can check for the use of UIWebView, which Apple has deprecated due to known security vulnerabilities and performance issues.

image

If you don’t have the source code, you can still check if the app uses UIWebView by running a simple command on the binary.

image

Javascript Bridges

From iOS 7 onwards, Apple provided APIs for communication between JavaScript in a WebView and native Swift or Objective-C objects. This integration is primarily facilitated through two methods:

The procedure for exploiting the functions starts with producing a JavaScript payload and injecting it into the file that the app is requesting. The injection can be accomplished via various techniques, for example:

Example on WebViews Javascript:

In order to get the secret from the Where's My Browser? app, you can use one of these techniques to inject the following payload that will reveal the secret by writing it to the “result” field of the WebView:

/**
 * JavaScript Bridge Callback Function
 * This function is called by native iOS code to return data to the web page
 * 
 * @param {string} name: The name/identifier of the callback or method being called
 * @param {string} value: The data/value returned from the native iOS side
 */
function javascriptBridgeCallBack(name, value) {
    // Update the HTML element with ID "result" to display the returned value
    // This is typically used to show the result of a native operation on the web page
    document.getElementById("result").innerHTML = value;
};

/**
 * Send a message from JavaScript to the native iOS WKWebView
 * This uses the WebKit message handler system to communicate with the iOS app
 */
window.webkit.messageHandlers.javaScriptBridge.postMessage(["getSecret"]);

// BREAKDOWN:
// window.webkit.messageHandlers: iOS WebKit's bridge for JavaScript-to-native communication
// .javaScriptBridge: The name of the message handler registered by the iOS app
// .postMessage(): Method to send data from JavaScript to native code
// ["getSecret"]: Array containing the command/message being sent to native code

/**
 * FLOW EXPLANATION:
 * 1. JavaScript sends "getSecret" command to iOS native code via postMessage()
 * 2. iOS app receives the message through WKScriptMessageHandler
 * 3. iOS processes the request (e.g., retrieves secret data)
 * 4. iOS calls back to JavaScript using javascriptBridgeCallBack() function
 * 5. The callback function updates the webpage with the result
 */

image

Conclusion

The pieces are now in place, and what a delightful set of toys we’ve assembled. Our exploration of iOS security has been like studying a worthy opponent’s Nen abilities. understanding code protection, third-party dependencies, IPC, and WebView bridges has revealed the application’s true potential for exploitation.

The magic lies not in individual techniques, but in how we combine them. Each vulnerability we’ve uncovered is like another playing card in our hand, waiting for the perfect moment to be deployed. The real performance begins in our next session, where we’ll step into the arena of practical labs. I can already feel the excitement building. there’s nothing quite like the thrill of testing one’s skills against a properly challenging application.

Stay sharp, and keep your Nen ready. The most engaging battles and the most valuable discoveries, await us in the labs.

Quote from Hisoka: A magician never reveals his secrets.