iOS All The Things - Part I

Featured image

Agenda of iOS Pentesting:

  1. Intro
  2. iOS Architecture
  3. IPA Architecture
  4. Programming iOS Apps
  5. Conclusion

Intro

Hey Geeks, and welcome to our ultimate guide! Ever wondered how hackers find weaknesses in ios apps, and how we can stop them? You’re in the right place.

This article is your complete roadmap, divided into more than one easy-to-follow part. We’ll break down everything about iOS penetration testing, from the basic concepts to the advanced tricks. And the best part? We’ll finish with a hands-on lab where we’ll solve challenges together, step-by-step.

So, whether you’re just curious or building your skills, let’s dive in and unlock the secrets of iOS security!

Now that we’re warmed up, let’s get a bit more specific. From a technical standpoint, iOS application penetration testing is a structured assessment of an iOS app’s security posture. It involves analyzing the app’s binary, its runtime behavior, and its communication with backend services to identify vulnerabilities that could lead to unauthorized access, data leakage, or compromise of user privacy.

This process typically covers several core areas:

The goal is to emulate a real-world attacker’s methodology to discover and help remediate risks, ensuring the app adheres to security best practices and protects sensitive user data effectively.

iOS Architecture

image

The first layer is the Core OS Layer. It is based on a Unix-like kernel, from which iOS inherits powerful low-level features and capabilities, such as a command-line interface and shell.

This layer acts as the direct conduit to the device’s hardware components, including Bluetooth, Wi-Fi, and various sensors. These components are accessed through secure, structured APIs.

The second layer is the Core Services Layer. It provides essential system services and acts as a intermediary between higher-level layers and the Core OS Layer. It does this by using the structured APIs provided by the Core OS Layer to request access to hardware components like Bluetooth, filesystem, and network connections.

For example, to access Bluetooth, a service in the Core Services layer would use its designated API to send a request down to the Core OS layer. The Core OS layer then translates that request into the specific instructions needed to activate the Bluetooth hardware.

The third layer is the Media Layer. This layer is responsible for all of the device’s graphics, audio, and video capabilities. It contains the powerful frameworks that applications use to display animations, play sounds, render 2D and 3D graphics, and play video files. While it provides the visual and auditory “style,” the actual user interface elements (like buttons and screens) are built using the higher-level Cocoa Touch layer.

The fourth layer is the Cocoa Touch Layer. This is the layer that users directly interact with. It provides the essential frameworks and infrastructure for building an app’s user interface, including multi-touch events, push notifications, and key app components like windows, views, and buttons. User-facing applications—such as Twitter, Messages, and Photos—are all built using the APIs and tools provided by this layer. It serves as the final, user-friendly abstraction over the complex lower layers of the iOS architecture.

Important Note: Apple’s iOS is a closed-source environment, unlike Android. You cannot install a modified iOS system because the hardware will reject it. All hardware components are cryptographically signed by Apple. Only the original iOS system has the correct digital certificate to communicate with the signed hardware. During boot, a “secure boot chain” process validates the iOS signature. If the signature is valid, the system boots; if not, it fails. This ensures only genuine Apple software can run on the device.

IPA Architecture

An IPA file is the application package format for iOS, functionally equivalent to an APK package on Android. When you extract an IPA package (e.g., test.ipa), you obtain its core contents. The key components include:

image

Info.plist

This is the Information Property List file, which serves a role similar to the AndroidManifest.xml in Android. It contains crucial metadata about the application, including:

The Executable (Mach-O Binary)

Extracting the IPA yields the main application executable. This is a Mach-O (Mach Object) file. This binary is the compiled, machine-code version of the application’s source code. Unlike a decompiled Android APK, which can often yield readable Java or Kotlin code, this Mach-O binary contains compiled code that cannot be directly read. To analyze it, you must use reverse engineering tools like Ghidra, Hopper Disassembler, or IDA Pro to disassemble it into assembly code. The goal is to analyze this assembly to understand the program’s logic and perform dynamic analysis during runtime.

This executable is protected by modern security controls such as:

Important Tip: The Decryption Hurdle of Any application downloaded from the official App Store has its main binary encrypted by Apple to protect intellectual property. If you try to analyze an App Store app directly with a disassembler, the tools will fail because they cannot read the encrypted code. To bypass this, a common technique is to run the app on a jailbroken device and dump the decrypted binary from memory (using tool like frida-ios-dump) before performing static analysis.

Frameworks Folder

This directory contains the libraries the application depends on. This includes:

Embedded.mobileprovision

This is a provisioning profile file. It contains the code-signing certificates and entitlements that authorize the app to run on specific devices or use certain Apple services. It also defines security policies and is crucial for understanding the app’s capabilities.

Sandbox and Permissions (Entitlements)

Sandbox: The core mechanism that prevents applications from accessing each other’s data. This is a mandatory access control system enforced by the iOS kernel that isolates each application into its own dedicated container. This design ensures that an app cannot read, write, or modify the contents of any other app’s container.

This isolation is physically implemented through three primary directory structures on the filesystem:

Permissions (Entitlements): The sandbox acts as a default-deny prison for applications. Entitlements are the signed permissions—embedded in the app’s code signature and provisioning profile that grant an app specific exceptions to these restrictions. They are the legal passes that allow an app to access protected resources like the camera, microphone, or network, outside of its own container.

Examples of Entitlements:

Programming iOS Apps

Developing applications for iOS is primarily done using two programming languages: Objective-C and Swift.

Language Definition
Objective-C This is an object-oriented programming language built as an extension of the standard C language. It was the primary language for iOS and macOS development for many years. While still maintained and used, especially in older codebases, it has largely been superseded by Swift for new projects.
Swift Introduced by Apple in 2014, Swift is a modern, fast, and type-safe programming language designed specifically for iOS, macOS, and other Apple ecosystem development. Its cleaner syntax and focus on safety make it the current recommended and most popular language for building new iOS applications.

The Primary Development Tool: Xcode

The central hub for iOS development is Xcode, which is directly comparable to Android Studio for Android development.

A Quick Tour of Swift:

Variable & constants

Swift uses let to declare constants and var to declare variables. Similar to languages like C, Swift uses these identifiers to store and refer to values in memory.

import Foundation

// Declare a variable that can change its value
var myVariable = 42
myVariable = 50

// Declare a constant that cannot be changed
let myConstant = 42
// myConstant = 50  // This would cause a compile-time error

print(myVariable) // equal 50
print(myVariable + myConstant) // equal 92

Swift is a type-safe language, which means it needs to know what type of data a variable can hold. While it can often infer the type automatically (called type inference), you can also explicitly specify it during declaration to ensure precision and clarity.

import Foundation

//Delcare the type
var test:String = "Ahmed"
print(test) //Ahmed 

Control Statements

Swift provides powerful control flow statements that let you conditionally execute code. The if statement is used for simple checks, while the switch statement is exceptionally powerful in Swift, capable of matching complex patterns and conditions far beyond simple integer values.

import Foundation

// If statements
let score = 85
if score > 80 {
    print("Great job!")
}

// For-in loop over an array
let names = ["Ahmed", "Ayman", "Ahmed"]
for name in names {
    print("Hello, \(name)!")
}

// Switch statement (powerful in Swift)
let vegetable = "red pepper"
switch vegetable {
case "celery":
    print("Add some raisins.")
case "cucumber", "watercress":
    print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
    print("Is it a spicy \(x)?") // This will print for "red pepper"
default:
    print("Everything tastes good in soup.")
}

Function

In Swift, a function is a self-contained block of code designed to perform a specific task, making it a fundamental building block for organizing and reusing code in iOS applications.

import Foundation

// Function
func test(x:String , y:String) -> String{
    return "Hi, \(x) \(y)"
}

var z = test(x:"Ahmed" , y:"Ayman")
print(z) // Hi, Ahmed Ayman

Class

In Swift, a class is a blueprint for creating objects. It encapsulates data (in the form of properties) and behavior (in the form of methods) that together describe a specific concept or entity.

import Foundation

// Class
class test{
    var num = 0
    func x() -> String{
        return "Done!"
    }
}

// Variable y is inherited all properties belong to class test()
var y = test()
// Can use any method or object in that class
print(y.num) // 0
print(y.x()) // Done!

Important Advice: A highly effective way to sharpen your penetration testing skills is to analyze intentionally vulnerable applications. Apps like DIVA-v2 are designed with security flaws for educational purposes.

Conclusion

That concludes Part one of our journey into iOS pentesting. We’ve laid the foundation by exploring the architecture, the IPA structure, and Swift Programming.

Just like Chrollo meticulously studies his opponents’ abilities before a fight, a successful pentester must first master the known system to exploit its unknown weaknesses.

Stay tuned. The real mission is about to begin.

Quote From Chrollo: In order to understand the unknown, one must first become acquainted with the known.