iOS Exploiting
Tip
AWS Hacking’i öğrenin ve pratik yapın:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın:HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking’i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter’da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
iOS Exploit Mitigations
1. Code Signing / Çalışma Zamanı İmza Doğrulaması
Early dönemde tanıtıldı (iPhone OS → iOS) Bu temel korumalardan biridir: tüm yürütülebilir kod (apps, dynamic libraries, JIT-ed code, extensions, frameworks, caches) Apple’ın güvenine dayalı bir sertifika zinciriyle kriptografik olarak imzalanmış olmalıdır. Çalışma zamanında, bir binary belleğe yüklenmeden önce (veya bazı sınırlar arası atlamalar yapılmadan önce), sistem imzayı kontrol eder. Kod değiştirilmişse (bit-flipped, patched) veya imzasızsa, yükleme başarısız olur.
- Engeller: exploit zincirlerindeki “klasik payload drop + execute” aşamasını; arbitrary code injection; mevcut bir binary’yi değiştirerek kötü amaçlı mantık eklemeyi.
- Mekanizma detayı:
- Mach-O loader (ve dynamic linker) kod sayfalarını, segmentleri, entitlements, team ID’leri ve imzanın dosya içeriğini kapsadığını kontrol eder.
- JIT cache veya dinamik oluşturulan kod gibi bellek bölgeleri için Apple, sayfaların imzalanmasını veya özel API’lerle doğrulanmasını zorunlu kılar (ör.
mprotectile code-sign kontrolleri). - İmza, entitlements ve identifier’ları içerir; OS bazı API’lerin veya ayrıcalıklı yeteneklerin belirli entitlements gerektirdiğini ve bunların sahte olamayacağını uygular.
Örnek
Bir exploit bir process’te code execution elde edip shellcode’u heap’e yazıp ona atlamaya çalıştığını varsayalım. iOS’ta o sayfanın executable olarak işaretlenmesi **ve** code-signature kısıtlarını karşılaması gerekir. Shellcode Apple’ın sertifikasıyla imzalanmadığı için atlama başarısız olur veya sistem o bellek bölgesinin executable yapılmasını reddeder.2. CoreTrust
iOS 14+ dönemi civarında tanıtıldı (veya daha yeni cihazlarda / daha sonraki iOS sürümlerinde kademeli) CoreTrust, ikili dosyaların (sistem ve kullanıcı binary’leri dahil) çalışma zamanında imza doğrulamasını yapan alt sistemdir ve cached userland trust store’lara güvenmek yerine Apple’ın root sertifikasına göre doğrulama yapar.
- Engeller: kurulum sonrası binary’lerin bozmasını, sistem kütüphanelerini veya kullanıcı uygulamalarını swap/patch ederek jailbreak yapma tekniklerini; trusted binary’leri kötü amaçlı karşılıklarıyla değiştirme girişimlerini.
- Mekanizma detayı:
- Yerel bir güven veritabanına veya sertifika önbelleğine güvenmek yerine, CoreTrust doğrudan Apple’ın root’una başvurur veya güvenli bir zincirde intermediate sertifikaları doğrular.
- Mevcut binary’lerdeki değişikliklerin (ör. filesystem’de) tespit edilip reddedilmesini sağlar.
- İmzalama sırasında entitlements, team ID’leri, code signing flag’leri ve diğer metadata’yı binary’ye bağlar ve yükleme sırasında OS bunu uygular.
Örnek
Bir jailbreak `SpringBoard` veya `libsystem`'i değiştirilmiş bir sürümle değiştirmeye çalışabilir. Ancak OS loader veya CoreTrust kontrol ettiğinde imza uyuşmazlığını (veya değiştirilmiş entitlements’ı) fark eder ve çalıştırmayı reddeder.3. Data Execution Prevention (DEP / NX / W^X)
Birçok OS’ta daha önce tanıtıldı; iOS uzun süredir NX-bit / w^x kullanıyor DEP, writable (veri) olarak işaretlenmiş sayfaların non-executable, executable olarak işaretlenmiş sayfaların ise non-writable olmasını zorunlu kılar. Basitçe heap veya stack bölgesine shellcode yazıp çalıştıramazsınız.
- Engeller: doğrudan shellcode execution; klasik buffer-overflow → injected shellcode’a atlama.
- Mekanizma detayı:
- MMU / bellek koruma flag’leri (sayfa tabloları aracılığıyla) ayrımı uygular.
- Writable bir sayfayı executable yapmak için yapılan her girişim sistem kontrolünü tetikler (veya yasaklanır ya da code-sign onayı gerektirir).
- Birçok durumda, sayfaları executable yapmak OS API’leri üzerinden yapılmalı ve ek kısıtlamalar ya da kontroller uygulanır.
Örnek
Bir overflow shellcode’u heap’e yazar. Saldırgan `mprotect(heap_addr, size, PROT_EXEC)` yapmaya çalışır. Ancak sistem bunu reddeder veya yeni sayfanın code-sign kısıtlarını karşılamasını zorunlu kılar (ki shellcode karşılamaz).4. Address Space Layout Randomization (ASLR)
iOS ~4–5 döneminde tanıtıldı ASLR, ana bellek bölgelerinin (libraries, heap, stack vb.) base adreslerini her process başlatıldığında rastgeleleştirir. Gadget adresleri çalıştırmalar arasında değişir.
- Engeller: ROP/JOP için sabit gadget adreslerini hardcode etme; statik exploit zincirleri; bilinen offset’lere kör atlamalar.
- Mekanizma detayı:
- Yüklenen her library / dynamic module rastgele bir offset’e rebase edilir.
- Stack ve heap base pointer’ları (belirli entropy limitleri içinde) randomize edilir.
- Bazen diğer bölgeler (ör. mmap allocations) da randomize edilir.
- Bilgi-disclosure mitigasyonları ile birlikte, saldırganın runtime’da base adreslerini öğrenmek için önce bir address veya pointer leak etmesi gerekir.
Örnek
Bir ROP zinciri `0x….lib + offset` adresinde bir gadget bekler. Ancak `lib` her çalıştırmada farklı yerleştirildiği için hardcoded zincir başarısız olur. Bir exploit önce modülün base adresini leak etmelidir, sonra gadget adreslerini hesaplayabilir.5. Kernel Address Space Layout Randomization (KASLR)
iOS ~ (iOS 5 / iOS 6 dönemi) Kullanıcı ASLR’sine benzer şekilde, KASLR kernel text ve diğer kernel yapılarını boot sırasında rastgeleleştirir.
- Engeller: kernel düzeyindeki exploit’lerin kernel kodunun veya verisinin sabit konumuna dayanmasını; statik kernel exploit’lerini.
- Mekanizma detayı:
- Her boot’ta kernel’in base adresi bir aralık içinde randomize edilir.
- Kernel veri yapıları (örn.
task_structs,vm_mapvb.) de taşınabilir veya offsetlenebilir. - Saldırganlar kernel pointer’larını önce leak etmeli veya bilgi ifşası zafiyeti kullanarak offsetleri hesaplamalıdır.
Örnek
Yerel bir zafiyet, kernel fonksiyon işaretçisi (örn. `vtable` içindeki) `KERN_BASE + offset` adresinde bozmaya çalışır. Ancak `KERN_BASE` bilinmediği için saldırgan önce onu leak etmelidir (örn. bir read primitive ile) ve sonra doğru adresi hesaplayıp bozma yapabilir.6. Kernel Patch Protection (KPP / AMCC)
Daha yeni iOS / A-series donanımda tanıtıldı (yaklaşık iOS 15–16 sonrası veya daha yeni çiplerde) KPP (aka AMCC), kernel text sayfalarının bütünlüğünü sürekli olarak izler (hash veya checksum ile). Eğer yetkisiz değişiklik (patch, inline hook, kod modifikasyonu) tespit edilirse kernel panic veya reboot tetikler.
- Engeller: kalıcı kernel patchleme (kernel talimatlarının değiştirilmesi), inline hook’lar, statik fonksiyon overwrite’ları.
- Mekanizma detayı:
- Bir donanım veya firmware modülü kernel text bölgesini izler.
- Periyodik veya talep üzerine sayfaları yeniden hash’ler ve beklenen değerlerle karşılaştırır.
- Uyumsuzluklar benign update pencereleri dışında oluşursa cihazı panic ile kapatır (kalıcı kötü amaçlı patch’ten kaçınmak için).
- Saldırganlar ya tespit pencelerini atlamalı ya da meşru patch yollarını kullanmalıdır.
Örnek
Bir exploit kernel fonksiyonun prologunu (örn. `memcmp`) patchlemeye çalışır. Ancak KPP, kod sayfasının hash’inin beklenenle uyuşmadığını görür ve kernel panic tetikleyerek cihazı çökertir, patch stabil hale gelmeden önce.7. Kernel Text Read‐Only Region (KTRR)
Modern SoC’larda tanıtıldı (yaklaşık A12 / daha yeni donanım sonrası) KTRR, donanım tarafından zorlanan bir mekanizmadır: kernel text erken boot sırasında kilitlenir ve EL1 (kernel) tarafından daha sonra yazılamaz hale getirilir, böylece kod sayfalarına yazma engellenir.
- Engeller: boot sonrası kernel kodunun herhangi bir şekilde değiştirilmesi (örn. patching, in-place code injection) EL1 ayrıcalığıyla yapılsa bile.
- Mekanizma detayı:
- Boot sırasında (secure/bootloader aşamasında), memory controller (veya güvenli bir donanım birimi) kernel text içeren fiziksel sayfaları read-only olarak işaretler.
- Bir exploit tam kernel ayrıcalığı elde etse bile bu sayfalara yazamaz.
- Bunları değiştirmek için saldırgan boot zincirini ele geçirmeli veya KTRR’yi altüst etmelidir.
Örnek
Bir privilege-escalation exploit EL1’e atlayıp bir kernel fonksiyonuna trampoline yazmaya çalışır (örn. `syscall` handler içinde). Ancak sayfalar KTRR tarafından read-only olarak kilitlendiği için yazma başarısız olur veya fault oluşur ve patch uygulanamaz.8. Pointer Authentication Codes (PAC)
ARMv8.3 ile (donanım), Apple A12 / iOS ~12+ ile başladı
- PAC, pointer değerlerinin (return adresleri, function pointer’lar, bazı data pointer’lar) manipülasyonunu tespit etmek için ARMv8.3-A ile gelen bir donanım özelliğidir; pointer’ın kullanılmayan üst bitlerine küçük bir kriptografik imza (bir “MAC”) gömülür.
- İmza (“PAC”), pointer değeri ile birlikte bir modifier (bağlam değeri, ör. stack pointer veya ayırıcı bir veri) üzerinden hesaplanır. Böylece aynı pointer değeri farklı bağlamlarda farklı PAC üretir.
- Kullanım anında, pointer’ı referans almadan veya o pointer üzerinden atlamadan önce bir authenticate talimatı PAC’i kontrol eder. Geçerliyse PAC temizlenir ve saf pointer alınır; geçersizse pointer “poisoned” olur (veya bir fault yükseltilir).
- PAC üretimi/doğrulaması için kullanılan anahtarlar ayrıcalıklı register’larda (EL1, kernel) yaşar ve user modundan doğrudan okunamaz.
- Birçok sistemde 64-bit pointer’ların tüm bitleri kullanılmadığı için (örn. 48-bit adres alanı), üst bitler PAC tutmak için kullanılabilir.
Mimari Temel & Anahtar Türleri
-
ARMv8.3 beş adet 128-bit anahtar tanıtır (her biri iki 64-bit sistem register’ı ile uygulanır).
-
APIAKey — instruction pointer’lar için (domain “I”, key A)
-
APIBKey — ikinci instruction pointer anahtarı (domain “I”, key B)
-
APDAKey — data pointer’lar için (domain “D”, key A)
-
APDBKey — data pointer’lar için (domain “D”, key B)
-
APGAKey — “generic” anahtar, pointer olmayan veriler veya diğer genel kullanımlar için
-
Bu anahtarlar ayrıcalıklı sistem register’larında saklanır (sadece EL1/EL2 vb. erişebilir), user modundan erişilemez.
-
PAC, kriptografik bir fonksiyonla (ARM genellikle QARMA önermektedir) şu girdilere göre hesaplanır:
- Pointer değeri (canonical bölüm)
- Bir modifier (bağlam değeri, örn. salt gibi)
- Gizli anahtar
- İçsel tweak mantığı Elde edilen PAC, pointer’ın üst bitlerinde saklanan değerle eşleşirse authentication başarılı olur.
Instruction Aileleri
İsimlendirme geleneği: PAC / AUT / XPAC, ardından domain harfleri.
PACxxtalimatları bir pointer’ı sign eder ve PAC eklerAUTxxtalimatları authenticate + strip yapar (PAC’i doğrular ve çıkarır)XPACxxtalimatları doğrulamadan strip eder
Domain / suffix açıklamaları:
| Mnemonic | Anlam / Domain | Key / Domain | Assembly’de Örnek Kullanım |
|---|---|---|---|
| PACIA | Instruction pointer’ı APIAKey ile imzala | “I, A” | PACIA X0, X1 — X0’daki pointer’ı APIAKey ile modifier X1 kullanarak imzala |
| PACIB | Instruction pointer’ı APIBKey ile imzala | “I, B” | PACIB X2, X3 |
| PACDA | Data pointer’ı APDAKey ile imzala | “D, A” | PACDA X4, X5 |
| PACDB | Data pointer’ı APDBKey ile imzala | “D, B” | PACDB X6, X7 |
| PACG / PACGA | Generic (non-pointer) imzalama APGAKey ile | “G” | PACGA X8, X9, X10 (X9’u modifier X10 ile X8’e imzala) |
| AUTIA | APIA ile imzalanmış instruction pointer’ı doğrula & PAC’i çıkar | “I, A” | AUTIA X0, X1 — X0’daki PAC’i X1 modifier ile kontrol et, sonra çıkar |
| AUTIB | APIB domain’ini doğrula | “I, B” | AUTIB X2, X3 |
| AUTDA | APDA ile imzalanmış data pointer’ı doğrula | “D, A” | AUTDA X4, X5 |
| AUTDB | APDB ile imzalanmış data pointer’ı doğrula | “D, B” | AUTDB X6, X7 |
| AUTGA | Generic / blob doğrulama (APGA) | “G” | AUTGA X8, X9, X10 (generic doğrulama) |
| XPACI | PAC’i strip et (instruction pointer, doğrulama yok) | “I” | XPACI X0 — X0’daki PAC’i kaldır (instruction domaine) |
| XPACD | PAC’i strip et (data pointer, doğrulama yok) | “D” | XPACD X4 — X4’teki data pointer’tan PAC’i kaldır |
Bazı özel / alias formlar:
PACIASPPACIA X30, SPiçin kısaltmadır (link register’ı SP ile modifier kullanarak imzala)AUTIASPAUTIA X30, SP’dir (link register’ı SP ile doğrula)RETAA,RETAB(authenticate-and-return) veyaBLRAA(authenticate & branch) gibi birleşik formlar ARM uzantılarında / compiler desteğinde bulunur.- Ayrıca modifier’ın sıfır olduğu
PACIZA/PACIZBgibi zero-modifier varyantları da vardır.
Modifier’lar
Modifier’ın ana hedefi PAC’i belirli bir bağlama bağlamaktır, böylece aynı adres farklı bağlamlarda farklı PAC üretir. Bu, pointer’ın farklı frame’lerde veya objelerde tekrar kullanımını zorlaştırır. Bir hash’e salt eklemeye benzer.
Buna göre:
- modifier bir bağlam değeri (başka bir register) olup PAC hesaplamasına karıştırılır. Tipik seçimler: stack pointer (
SP), frame pointer veya bir object ID. - SP’i modifier olarak kullanmak, return address signing için yaygındır: PAC belirli stack frame’e bağlanır. Aynı LR’ı farklı bir frame’de yeniden kullanmaya çalışırsanız modifier değişir ve PAC doğrulaması başarısız olur.
- Aynı pointer değeri farklı modifier’larla imzalandığında farklı PAC’ler üretilir.
- Modifier gizli olmak zorunda değildir, ancak ideal olarak saldırgan tarafından kontrol edilmemelidir.
- Anlamlı bir modifier yoksa bazı talimatlar implicit sıfır veya sabit kullanır.
Apple / iOS / XNU Özelleştirmeleri & Gözlemler
- Apple’ın PAC uygulaması boot-başına diversifier’lar içerir; böylece anahtarlar veya tweak’ler her bootta değişir ve boot’lar arasında yeniden kullanım engellenir.
- Ayrıca cross-domain mitigasyonları uygulanır, böylece user modunda imzalanmış PAC’ler kernel modunda kolayca yeniden kullanılamaz.
- Apple Silicon (M1) üzerinde tersine mühendislik, dokuz modifier tipi ve Apple’a özgü sistem register’ları olduğunu gösterdi.
- Apple, PAC’i birçok kernel alt sisteminde kullanır: return address signing, kernel veri pointer bütünlüğü, signed thread context’leri vb.
- Google Project Zero, güçlü bir memory read/write primitive ile kernel PAC’leri (A anahtarları için) A12 döneminde sahteleyebileceğini gösterdi; Apple bu yolların çoğunu yamadı.
- Apple’ın sisteminde bazı anahtarlar kernel genelinde iken, user process’ler per-process anahtar rastgeleliği alabilir.
PAC Bypass’ları
- Kernel-mode PAC: teorik vs gerçek bypass’lar
- Kernel PAC anahtarları ve mantığı sıkı kontrol edildiği için (ayrıcalıklı register’lar, diversifier’lar, domain izolasyonu), sahte kernel signed pointer’lar üretmek oldukça zordur.
- Azad’ın 2020 “iOS Kernel PAC, One Year Later” raporu iOS 12-13’te birkaç kısmi bypass (signing gadget’lar, signed state reuse, korumasız indirect branch’ler) bulduğunu rapor etti ancak genel amaçlı tam bir bypass bulamadı. bazad.github.io
- Apple’ın “Dark Magic” özelleştirmeleri exploitable yüzeyi daha da daralttı (domain switching, per-key enable bit’leri). i.blackhat.com
- Apple silicon (M1/M2) için Zecao Cai ve ekibi tarafından raporlanmış bilinen bir kernel PAC bypass CVE-2023-32424 vardır. i.blackhat.com
- Ancak bu bypass’lar genellikle çok spesifik gadget’lara veya implementasyon hatalarına dayanır; genel amaçlı değildir.
Bu nedenle kernel PAC, yüksek derecede sağlam kabul edilir, fakat kusursuz değildir.
- User-mode / runtime PAC bypass teknikleri
Bunlar daha yaygındır ve PAC’in uygulanmasındaki veya runtime framework’lerdeki eksiklikleri suistimal eder. Aşağıda sınıflar ve örnekler verilmiştir.
2.1 Shared Cache / A key sorunları
- dyld shared cache büyük, ön bağlanmış system framework ve library blob’udur. Çok yaygın paylaşıldığı için, shared cache içindeki function pointer’lar “önceden imzalanmış” olur ve birçok process tarafından kullanılır. Saldırganlar bu zaten-imzalanmış pointer’ları “PAC oracle” olarak hedefler.
- Bazı bypass teknikleri shared cache’te bulunan A-key ile imzalanmış pointer’ları çıkarmaya veya yeniden kullanmaya çalışır.
- “No Clicks Required” sunumu, shared cache üzerinde bir oracle kurarak relatif adresleri çıkarmayı ve imzalanmış pointer’larla birleştirerek PAC’ı aşmayı anlatır. saelo.github.io
- Ayrıca, userspace’de shared libraries’den import edilen function pointer’ların PAC tarafından yeterince korunmadığı bulunmuş, bu da saldırganın function pointer’ları değiştirmeden elde etmesine izin veriyor. (Project Zero bug entry) bugs.chromium.org
2.2 dlsym(3) / dynamic symbol resolution
- Bilinen bir bypass,
dlsym()çağırıp zaten imzalanmış bir function pointer almak ve onu kullanmaktır (A-key ile, diversifier sıfır olabilir).dlsymmeşru imzalanmış bir pointer döndürdüğünde, onu kullanmak PAC’i sahteleştirme ihtiyacını ortadan kaldırır. - Epsilon’un blogu bazı bypass’ların bunu nasıl kullandığını detaylandırır:
dlsym("someSym")çağrısı imzalı bir pointer döndürür ve dolaylı çağrılar için kullanılabilir. blog.epsilon-sec.com - Synacktiv’in “iOS 18.4 — dlsym considered harmful” yazısı bir hata anlatır: iOS 18.4’te
dlsymile çözümlenen bazı semboller yanlış imzalanmış pointer’lar döndürüyor (veya hatalı diversifier), bu da istenmeyen PAC bypass’larına yol açıyor. Synacktiv - dyld içindeki mantık şuna benzer:
result->isCodeolduğunda döndürülen pointer__builtin_ptrauth_sign_unauthenticated(..., key_asia, 0)ile sign edilir, yani bağlam sıfır. blog.epsilon-sec.com
Bu yüzden dlsym user-mode PAC bypass’larında sıkça kullanılan bir vektördür.
2.3 Diğer DYLD / runtime relocation’ları
- DYLD loader ve dynamic relocation mantığı karmaşıktır ve bazen relocasyon yapmak için geçici olarak sayfaları read/write olarak map eder, sonra tekrar read-only’a çevirir. Saldırganlar bu pencereleri suistimal eder. Synacktiv’in sunumu “Operation Triangulation” adlı, dynamic relocation’lar üzerinden timing tabanlı bir PAC bypass’ını anlatır. Synacktiv
- DYLD sayfaları artık SPRR / VM_FLAGS_TPRO gibi korumalarla korunuyor. Ancak önceki sürümlerde daha zayıf korumalar vardı. Synacktiv
- WebKit exploit zincirlerinde, DYLD loader sıklıkla PAC bypass hedefi olmuştur. Slaytlar, birçok PAC bypass’ının DYLD loader’ı hedeflediğini (relocation, interposer hooks yoluyla) belirtir. Synacktiv
2.4 NSPredicate / NSExpression / ObjC / SLOP
- Userland exploit zincirlerinde, Objective-C runtime metotları (örn.
NSPredicate,NSExpression,NSInvocation) kontrol çağrılarını gizli şekilde geçirmek için kullanılır. - PAC öncesi eski iOS’ta bir exploit, fake
NSInvocationobjeleri kullanarak kontrollü bellekteki herhangi bir selector’ı çağırmaya izin veriyordu. PAC ile teknikler değişiklik gerektirdi; ancak SLOP (SeLector Oriented Programming) PAC altında da genişletildi. Project Zero - Orijinal SLOP tekniği, sahte invocations oluşturarak ObjC çağrıları zincirlemeye izin veriyordu; bypass, ISA veya selector pointer’larının bazen tam olarak PAC ile korunmaması gerçeğine dayanıyordu. Project Zero
- Pointer authentication kısmen uygulanan ortamlarda, metodlar / selector’lar / hedef pointer’lar her zaman PAC korunmuyor olabilir; bu da bypass için alan açar.
Örnek Akış
Örnek İmzalama & Doğrulama
``` ; Example: function prologue / return address protection my_func: stp x29, x30, [sp, #-0x20]! ; push frame pointer + LR mov x29, sp PACIASP ; sign LR (x30) using SP as modifier ; … body … mov sp, x29 ldp x29, x30, [sp], #0x20 ; restore AUTIASP ; authenticate & strip PAC ret; Example: indirect function pointer stored in a struct ; suppose X1 contains a function pointer PACDA X1, X2 ; sign data pointer X1 with context X2 STR X1, [X0] ; store signed pointer
; later retrieval: LDR X1, [X0] AUTDA X1, X2 ; authenticate & strip BLR X1 ; branch to valid target
; Example: stripping for comparison (unsafe) LDR X1, [X0] XPACI X1 ; strip PAC (instruction domain) CMP X1, #some_label_address BEQ matched_label
</details>
<details>
<summary>Example</summary>
A buffer overflow, yığıt üzerindeki bir return adresini üzerine yazar. Saldırgan hedef gadget adresini yazar fakat doğru PAC'i hesaplayamaz. Fonksiyon döndüğünde CPU’nun `AUTIA` talimatı PAC uyuşmazlığı nedeniyle hata verir. Zincir başarısız olur.
Project Zero’nun A12 (iPhone XS) üzerindeki analizi, Apple’ın PAC kullanımını ve bir saldırganın memory read/write primitive’a sahip olması durumunda PAC’leri sahtelemeye yönelik yöntemleri gösterdi.
</details>
### 9. **Branch Target Identification (BTI)**
**ARMv8.5 ile tanıtıldı (daha yeni donanım)**
BTI, **dolaylı branch hedeflerini** kontrol eden bir donanım özelliğidir: `blr` veya dolaylı call/jump çalıştırıldığında, hedef bir **BTI landing pad** (`BTI j` veya `BTI c`) ile başlamalıdır. Landing pad olmayan gadget adreslerine atlama bir istisna tetikler.
LLVM’nin uygulaması BTI talimatlarının üç varyantını ve bunların branch türlerine nasıl eşlendiğini not eder.
| BTI Variant | What it permits (which branch types) | Typical placement / use case |
|-------------|----------------------------------------|-------------------------------|
| **BTI C** | Targets of *call*-style indirect branches (e.g. `BLR`, or `BR` using X16/X17) | Put at entry of functions that may be called indirectly |
| **BTI J** | Targets of *jump*-style branches (e.g. `BR` used for tail calls) | Placed at the beginning of blocks reachable by jump tables or tail-calls |
| **BTI JC** | Acts as both C and J | Can be targeted by either call or jump branches |
- Branch target enforcement ile derlenmiş kodda, derleyiciler her geçerli dolaylı-branch hedefinde (fonksiyon başlangıçları veya jump ile ulaşılabilen bloklar) bir BTI talimatı (C, J veya JC) ekler, böylece dolaylı branchler yalnızca bu yerlere başarılı olur.
- **Direct branches / calls** (yani sabit-adres `B`, `BL`) BTI tarafından **kısıtlanmaz**. Varsayım, kod sayfalarının güvenilir olduğu ve saldırganın bunları değiştiremeyeceğidir (dolayısıyla direct branchler güvenlidir).
- Ayrıca, **RET / return** talimatları genellikle BTI ile kısıtlanmaz çünkü return adresleri PAC veya return signing mekanizmalarıyla korunur.
#### Mechanism and enforcement
- CPU, bir sayfa “guarded / BTI-enabled” olarak işaretlenmişken bir **dolaylı branch (BLR / BR)** kodunu decode ettiğinde, hedef adresin ilk talimatının geçerli bir BTI (izin verilen C, J veya JC) olup olmadığını kontrol eder. Değilse, bir **Branch Target Exception** oluşur.
- BTI talimatı enkodlaması, daha önce NOP için ayrılmış opcode’ları yeniden kullanacak şekilde tasarlanmıştır (önceki ARM sürümlerinde). Bu yüzden BTI-etkin ikili dosyalar geriye dönük uyumludur: BTI desteklemeyen donanımda bu talimatlar NOP gibi davranır.
- BTI ekleyen derleyici pass’leri yalnızca gerektiği yerde ekleme yapar: dolaylı olarak çağrılabilecek fonksiyonların girişlerine veya jump’larla hedeflenebilen basic block’lara.
- Bazı yamalar ve LLVM kodu gösterir ki BTI her basic block için değil — yalnızca potansiyel branch hedefleri (ör. switch / jump tablolarından gelenler) için eklenir.
#### BTI + PAC synergy
PAC pointer değerini (kaynağı) korur — dolaylı çağrıların/return’lerin zincirinin değiştirilmediğini garanti eder.
BTI, geçerli bir pointer’ın yalnızca doğru işaretlenmiş giriş noktalarına hedef olmasını sağlar.
Birlikte ele alındığında, bir saldırganın hem doğru PAC ile imzalanmış geçerli bir pointer’a hem de hedefte BTI’nin bulunmasına ihtiyacı olur. Bu, exploit gadget’ları oluşturmayı zorlaştırır.
#### Example
<details>
<summary>Example</summary>
Bir exploit, `0xABCDEF` adresindeki ve `BTI c` ile başlamayan bir gadget’a pivot etmeye çalışır. CPU, `blr x0` çalıştırıldığında hedefi kontrol eder ve talimat hizalaması geçerli bir landing pad içermediği için hata verir. Bu nedenle birçok gadget, BTI öneki yoksa kullanılamaz hale gelir.
</details>
### 10. **Privileged Access Never (PAN) & Privileged Execute Never (PXN)**
**ARMv8 uzantılarının daha yeni sürümlerinde / iOS desteğinde tanıtıldı (hardened kernel için)**
#### PAN (Privileged Access Never)
- **PAN**, **ARMv8.1-A** ile tanıtılan bir özellik olup **ayrıcalıklı kodun** (EL1 veya EL2) **kullanıcı erişimli (EL0)** olarak işaretlenmiş belleği **okumasını veya yazmasını** engeller; PAN açık değilse erişime izin verilmez.
- Fikir: kernel kandırılsa veya kontrolü ele geçse bile, user-space pointer’ları rastgele dereference edememeli; PAN’ı açıkça temizlemeden önce bu erişimler engellenir — bu, **ret2usr** tarzı exploit’ler veya kullanıcı kontrollü buffer’ların kötüye kullanımını azaltır.
- PAN etkin olduğunda (PSTATE.PAN = 1), bir ayrıcalıklı load/store talimatı EL0 tarafından “erişilebilir” olarak işaretlenmiş bir sanal adrese erişmeye çalışırsa bir **permission fault** tetiklenir.
- Kernel, meşru olarak user-space belleğe erişmesi gerektiğinde (ör. user buffer’larına veri kopyalama), bu erişime izin vermek için **geçici olarak PAN’ı devre dışı bırakmalı** (veya “unprivileged load/store” talimatlarını kullanmalıdır).
- Linux on ARM64’te PAN desteği yaklaşık 2015 civarında eklendi: kernel yamaları özelliğin tespitini ekledi ve `get_user` / `put_user` gibi fonksiyonları PAN’ı temizleyen varyantlarla değiştirdi.
**Önemli nüans / sınırlama / bug**
- Siguza ve diğerlerinin belirttiği gibi, ARM tasarımındaki bir spesifikasyon hatası (veya belirsiz davranış), **execute-only user mappings** (`--x`) PAN’ı tetiklemeyebilir. Başka bir deyişle, bir user sayfası yürütülebilir olarak işaretlenmiş fakat okuma izni yoksa, kernel’in okuma girişimi PAN’ı atlayabilir çünkü mimari “EL0 tarafından erişilebilir” olarak okunabilir iznini gerektiriyor, sadece yürütülebilirliği değil. Bu, belirli konfigürasyonlarda PAN atlatmasına yol açar.
- Bu yüzden, eğer iOS / XNU execute-only user sayfalarına izin veriyorsa (ör. bazı JIT veya code-cache kurulumları), kernel PAN etkin olsa bile bu sayfalardan kazara okuyabilir. Bu, bazı ARMv8+ sistemlerde bilinen hassas bir exploitable alandır.
#### PXN (Privileged eXecute Never)
- **PXN**, sayfa tablosu girişlerinde (leaf veya block entry’lerde) bulunan bir bayraktır ve sayfanın **ayrıcalıklı modda yürütülemez** olduğunu belirtir (yani EL1 yürütürken).
- PXN, kernel’in (veya herhangi bir ayrıcalıklı kodun) user-space sayfalarından atlama yapmasını veya bu sayfalardaki talimatları yürütmesini engeller; sonuç olarak, kernel seviyesinde kontrol akışının user belleğine yönlendirilmesini durdurur.
- PAN ile birlikte:
1. Kernel varsayılan olarak user-space veriyi okuyamaz/ yazamaz (PAN)
2. Kernel user-space kodunu çalıştıramaz (PXN)
- ARMv8 sayfa tablosu formatında, leaf girişlerin attribute bitleri içinde `PXN` biti (ve ayrıca `UXN` unprivileged execute-never için) bulunur.
Dolayısıyla, kernel bozuk bir fonksiyon pointer’ı user belleğe işaret etse ve oraya branch denese bile, PXN biti bir hata tetikleyecektir.
#### Memory-permission model & how PAN and PXN map to page table bits
PAN / PXN’in nasıl çalıştığını anlamak için ARM’ın çeviri ve izin modeline bakmak gerekir (basitleştirilmiş):
- Her sayfa veya block girişi, AP[2:1] gibi erişim izinleri (oku/yaz, ayrıcalıklı vs ayrılmamış) ve execute-never kısıtlamaları için UXN / PXN bitleri dahil attribute alanlarına sahiptir.
- PSTATE.PAN 1 iken donanım değiştirilmiş semantiği uygular: EL0 tarafından “erişilebilir” (yani user-accessible) olarak işaretlenmiş sayfalara ayrıcalıklı erişimler engellenir (fault).
- Bahsedilen bug nedeniyle, yalnızca yürütülebilir (okunamaz) olarak işaretlenmiş sayfalar bazı implementasyonlarda EL0 tarafından “erişilebilir” sayılmaz kabul edilebilir ve bu da PAN’ı atlayabilir.
- Bir sayfanın PXN biti setliyse, talimat fetch’i daha yüksek ayrıcalık seviyesinden gelse bile yürütme yasaklanır.
#### Kernel usage of PAN / PXN in a hardened OS (e.g. iOS / XNU)
Sertleştirilmiş bir kernel tasarımında (Apple’ın kullandığı gibi):
- Kernel varsayılan olarak PAN’ı etkinleştirir (böylece ayrıcalıklı kod kısıtlanır).
- Meşru olarak user buffer’larını okumak/yazmak gerektiğinde (ör. syscall buffer copy, I/O, read/write user pointer), kernel geçici olarak **PAN’ı devre dışı bırakır** veya bu erişimleri sağlamak için özel talimatlar kullanır.
- User sayfalar için sayfa tablolarında PXN = 1 olarak uygulanır: böylece kernel bu sayfaları çalıştıramaz; kernel sayfaları ise PXN içermez.
- Kernel, hiçbir kod yolunun execution flow’u user bellek bölgelerine yönlendirmesine izin verilmediğinden emin olmalıdır (bu, PXN’yi atlayacak yolları engeller) — bu şekilde “kullanıcı kontrollü shellcode’a atlama”ya dayanan exploit zincirleri engellenir.
Bahsedilen execute-only sayfalar aracılığıyla PAN atlatması nedeniyle, gerçek bir sistemde Apple execute-only user sayfalarını devre dışı bırakabilir veya spesifikasyon zayıflığı etrafında yamalar uygulayabilir.
#### Attack surfaces, bypasses, and mitigations
- **PAN bypass via execute-only pages**: daha önce tartışıldığı gibi, spesifikasyon bir boşluk bırakır: yalnızca execute-only (okuma izni olmayan) user sayfaları bazı implementasyonlarda “EL0 tarafından erişilebilir” sayılmayabilir, bu yüzden PAN kernel’in bu sayfalardan okumasını engellemez. Bu, saldırgana execute-only bölümler aracılığıyla veri besleme için alışılmadık bir yol sağlar.
- **Temporal window exploit**: kernel PAN’ı gerektiğinden daha uzun süre devre dışı bırakırsa, bir yarış veya kötü amaçlı yol bu pencereyi kullanarak istenmeyen user bellek erişimleri gerçekleştirebilir.
- **Forgotten re-enable**: kod yolları PAN’ı tekrar etkinleştirmeyi unutursa, sonraki kernel işlemleri yanlışlıkla user belleğe erişebilir.
- **Misconfiguration of PXN**: sayfa tabloları user sayfalarda PXN’yi setlemezse veya user kod sayfalarını yanlış haritalarsa, kernel user-space kodunu çalıştırmaya kandırılabilir.
- **Speculation / side-channels**: spekülatif atlama gibi analog olarak, mikro-mimari yan etkiler PAN / PXN kontrollerinin geçici ihlaline neden olabilir (ancak bu tür saldırılar CPU tasarımına çok bağlıdır).
- **Complex interactions**: JIT, shared memory, just-in-time code region’lar gibi gelişmiş özelliklerde kernel, user-mapped bölgelerde bazı bellek erişimlerine veya yürütmeye izin vermek zorunda kalabilir; PAN/PXN kısıtları altında bunları güvenli tasarlamak zor ve hataya açıktır.
#### Example
<details>
<summary>Code Example</summary>
Here are illustrative pseudo-assembly sequences showing enabling/disabling PAN around user memory access, and how a fault might occur.
// Suppose kernel entry point, PAN is enabled (privileged code cannot access user memory by default)
; Kernel receives a syscall with user pointer in X0 ; wants to read an integer from user space mov X1, X0 ; X1 = user pointer
; disable PAN to allow privileged access to user memory MSR PSTATE.PAN, #0 ; clear PAN bit, disabling the restriction
ldr W2, [X1] ; now allowed load from user address
; re-enable PAN before doing other kernel logic MSR PSTATE.PAN, #1 ; set PAN
; … further kernel work …
; Later, suppose an exploit corrupts a pointer to a user-space code page and jumps there BR X3 ; branch to X3 (which points into user memory)
; Because the target page is marked PXN = 1 for privileged execution, ; the CPU throws an exception (fault) and rejects execution
If the kernel had **not** set PXN on that user page, then the branch might succeed — which would be insecure.
Eğer kernel o kullanıcı sayfasında PXN ayarlamamış olsaydı, dallanma başarılı olabilir — bu ise güvensiz olurdu.
If the kernel forgets to re-enable PAN after user memory access, it opens a window where further kernel logic might accidentally read/write arbitrary user memory.
Kernel, kullanıcı belleğine erişimden sonra PAN'ı yeniden etkinleştirmeyi unutursa, sonraki kernel mantığının kazara rasgele kullanıcı belleğini okumasına/yazmasına izin veren bir pencere açılır.
If the user pointer is into an execute-only page (user page with only execute permission, no read/write), under the PAN spec bug, `ldr W2, [X1]` might **not** fault even with PAN enabled, enabling a bypass exploit, depending on implementation.
Eğer kullanıcı işaretçisi execute-only bir sayfaya işaret ediyorsa (sadece execute izni olan, okuma/yazma olmayan kullanıcı sayfası), PAN spesifikasyon hatası altında `ldr W2, [X1]` PAN etkin olsa bile hata vermeyebilir; bu, uygulamaya bağlı olarak bir bypass exploit'ine olanak tanır.
</details>
<details>
<summary>Example</summary>
A kernel vulnerability tries to take a user-provided function pointer and call it in kernel context (i.e. `call user_buffer`). Under PAN/PXN, that operation is disallowed or faults.
Bir kernel açığı, kullanıcı tarafından sağlanan bir fonksiyon işaretçisini alıp kernel bağlamında çağırmaya çalışır (ör. `call user_buffer`). PAN/PXN altında bu işlem ya yasaklanır ya da hata verir.
</details>
---
### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introduced in ARMv8.5 / newer (or optional extension)**
TBI means the top byte (most-significant byte) of a 64-bit pointer is ignored by address translation. This lets OS or hardware embed **tag bits** in the pointer’s top byte without affecting the actual address.
- TBI stands for **Top Byte Ignore** (sometimes called *Address Tagging*). It is a hardware feature (available in many ARMv8+ implementations) that **ignores the top 8 bits** (bits 63:56) of a 64-bit pointer when performing **address translation / load/store / instruction fetch**.
- In effect, the CPU treats a pointer `0xTTxxxx_xxxx_xxxx` (where `TT` = top byte) as `0x00xxxx_xxxx_xxxx` for the purposes of address translation, ignoring (masking off) the top byte. The top byte can be used by software to store **metadata / tag bits**.
- This gives software “free” in-band space to embed a byte of tag in each pointer without altering which memory location it refers to.
- The architecture ensures that loads, stores, and instruction fetch treat the pointer with its top byte masked (i.e. tag stripped off) before performing the actual memory access.
Thus TBI decouples the **logical pointer** (pointer + tag) from the **physical address** used for memory operations.
#### Why TBI: Use cases and motivation
- **Pointer tagging / metadata**: You can store extra metadata (e.g. object type, version, bounds, integrity tags) in that top byte. When you later use the pointer, the tag is ignored at hardware level, so you don’t need to strip manually for the memory access.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI is the base hardware mechanism that MTE builds on. In ARMv8.5, the **Memory Tagging Extension** uses bits 59:56 of the pointer as a **logical tag** and checks it against an **allocation tag** stored in memory.
- **Enhanced security & integrity**: By combining TBI with pointer authentication (PAC) or runtime checks, you can force not just the pointer value but also the tag to be correct. An attacker overwriting a pointer without the correct tag will produce a mismatched tag.
- **Compatibility**: Because TBI is optional and tag bits are ignored by hardware, existing untagged code continues to operate normally. The tag bits effectively become “don’t care” bits for legacy code.
#### Example
<details>
<summary>Example</summary>
A function pointer included a tag in its top byte (say `0xAA`). An exploit overwrites the pointer low bits but neglects the tag, so when the kernel verifies or sanitizes, the pointer fails or is rejected.
Bir fonksiyon işaretçisinin üst baytında bir tag vardı (örneğin `0xAA`). Bir exploit işaretçinin düşük bitlerini overwrite eder ama tag'ı değiştirmez; kernel doğrulama veya sanitasyon yaptığında işaretçi başarısız olur veya reddedilir.
</details>
---
### 12. **Page Protection Layer (PPL)**
**Introduced in late iOS / modern hardware (iOS ~17 / Apple silicon / high-end models)** (some reports show PPL circa macOS / Apple silicon, but Apple is bringing analogous protections to iOS)
- PPL is designed as an **intra-kernel protection boundary**: even if the kernel (EL1) is compromised and has read/write capabilities, **it should not be able to freely modify** certain **sensitive pages** (especially page tables, code-signing metadata, kernel code pages, entitlements, trust caches, etc.).
- It effectively creates a **“kernel within the kernel”** — a smaller trusted component (PPL) with **elevated privileges** that alone can modify protected pages. Other kernel code must call into PPL routines to effect changes.
- This reduces the attack surface for kernel exploits: even with full arbitrary R/W/execute in kernel mode, exploit code must also somehow get into the PPL domain (or bypass PPL) to modify critical structures.
- On newer Apple silicon (A15+ / M2+), Apple is transitioning to **SPTM (Secure Page Table Monitor)**, which in many cases replaces PPL for page-table protection on those platforms.
Here’s how PPL is believed to operate, based on public analysis:
#### Use of APRR / permission routing (APRR = Access Permission ReRouting)
- Apple hardware uses a mechanism called **APRR (Access Permission ReRouting)**, which allows page table entries (PTEs) to contain small indices, rather than full permission bits. Those indices are mapped via APRR registers to actual permissions. This allows dynamic remapping of permissions per domain.
- PPL leverages APRR to segregate privilege within kernel context: only the PPL domain is permitted to update the mapping between indices and effective permissions. That is, when non-PPL kernel code writes a PTE or tries to flip permission bits, the APRR logic disallows it (or enforces read-only mapping).
- PPL code itself runs in a restricted region (e.g. `__PPLTEXT`) which is normally non-executable or non-writable until entry gates temporarily allow it. The kernel calls PPL entry points (“PPL routines”) to perform sensitive operations.
#### Gate / Entry & Exit
- When the kernel needs to modify a protected page (e.g. change permissions of a kernel code page, or modify page tables), it calls into a **PPL wrapper** routine, which does validation and then transitions into the PPL domain. Outside that domain, the protected pages are effectively read-only or non-modifiable by the main kernel.
- During PPL entry, the APRR mappings are adjusted so that memory pages in the PPL region are set to **executable & writable** within PPL. Upon exit, they are returned to read-only / non-writable. This ensures that only well-audited PPL routines can write to protected pages.
- Outside PPL, attempts by kernel code to write to those protected pages will fault (permission denied) because the APRR mapping for that code domain doesn’t permit writing.
#### Protected page categories
The pages that PPL typically protects include:
- Page table structures (translation table entries, mapping metadata)
- Kernel code pages, especially those containing critical logic
- Code-sign metadata (trust caches, signature blobs)
- Entitlement tables, signature enforcement tables
- Other high-value kernel structures where a patch would allow bypassing signature checks or credentials manipulation
The idea is that even if the kernel memory is fully controlled, the attacker cannot simply patch or rewrite these pages, unless they also compromise PPL routines or bypass PPL.
#### Known Bypasses & Vulnerabilities
1. **Project Zero’s PPL bypass (stale TLB trick)**
- A public writeup by Project Zero describes a bypass involving **stale TLB entries**.
- The idea:
1. Allocate two physical pages A and B, mark them as PPL pages (so they are protected).
2. Map two virtual addresses P and Q whose L3 translation table pages come from A and B.
3. Spin a thread to continuously access Q, keeping its TLB entry alive.
4. Call `pmap_remove_options()` to remove mappings starting at P; due to a bug, the code mistakenly removes the TTEs for both P and Q, but only invalidates the TLB entry for P, leaving Q’s stale entry live.
5. Reuse B (page Q’s table) to map arbitrary memory (e.g. PPL-protected pages). Because the stale TLB entry still maps Q’s old mapping, that mapping remains valid for that context.
6. Through this, the attacker can put writable mapping of PPL-protected pages in place without going through PPL interface.
- This exploit required fine control of physical mapping and TLB behavior. It demonstrates that a security boundary relying on TLB / mapping correctness must be extremely careful about TLB invalidations and mapping consistency.
- Project Zero commented that bypasses like this are subtle and rare, but possible in complex systems. Still, they regard PPL as a solid mitigation.
2. **Other potential hazards & constraints**
- If a kernel exploit can directly enter PPL routines (via calling the PPL wrappers), it might bypass restrictions. Thus argument validation is critical.
- Bugs in the PPL code itself (e.g. arithmetic overflow, boundary checks) can allow out-of-bounds modifications inside PPL. Project Zero observed that such a bug in `pmap_remove_options_internal()` was exploited in their bypass.
- The PPL boundary is irrevocably tied to hardware enforcement (APRR, memory controller), so it's only as strong as the hardware implementation.
#### Example
<details>
<summary>Code Example</summary>
Here’s a simplified pseudocode / logic showing how a kernel might call into PPL to modify protected pages:
Aşağıda, bir kernel'in korunmuş sayfaları değiştirmek için PPL'e nasıl çağrı yapabileceğini gösteren basitleştirilmiş bir pseudocode / mantık örneği bulunmaktadır:
</details>
```c
// In kernel (outside PPL domain)
function kernel_modify_pptable(pt_addr, new_entry) {
// validate arguments, etc.
return ppl_call_modify(pt_addr, new_entry) // call PPL wrapper
}
// In PPL (trusted domain)
function ppl_call_modify(pt_addr, new_entry) {
// temporarily enable write access to protected pages (via APRR adjustments)
aprr_set_index_for_write(PPL_INDEX)
// perform the modification
*pt_addr = new_entry
// restore permissions (make pages read-only again)
aprr_restore_default()
return success
}
// If kernel code outside PPL does:
*pt_addr = new_entry // a direct write
// It will fault because APRR mapping for non-PPL domain disallows write to that page
Çekirdek davranışı
Çekirdek birçok normal işlemi yapabilir, ancak korumalı mapping'leri değiştirmek veya kod yama yapmak yalnızca `ppl_call_*` rutinleri aracılığıyla mümkündür.Örnek
A kernel exploit, entitlement table'ı overwrite etmeye veya kernel signature blob'u değiştirerek code-sign enforcement'ı devre dışı bırakmaya çalışır. O sayfa PPL-protected olduğundan, yazma PPL arayüzü üzerinden yapılmadıkça engellenir. Yani kernel kodu çalıştırma olsa bile code-sign kısıtlamalarını atlayamaz veya credential data'yı rastgele değiştiremezsiniz. iOS 17+ üzerinde belirli cihazlar, PPL tarafından yönetilen sayfaları daha da izole etmek için SPTM kullanır.PPL → SPTM / Replacements / Future
- On Apple’s modern SoCs (A15 or later, M2 or later), Apple supports SPTM (Secure Page Table Monitor), which replaces PPL for page table protections.
- Apple calls out in documentation: “Page Protection Layer (PPL) and Secure Page Table Monitor (SPTM) enforce execution of signed and trusted code … PPL manages the page table permission overrides … Secure Page Table Monitor replaces PPL on supported platforms.”
- The SPTM architecture likely shifts more policy enforcement into a higher-privileged monitor outside kernel control, further reducing the trust boundary.
MTE | EMTE | MIE
Aşağıda EMTE’nin Apple’ın MIE düzeni altında nasıl çalıştığına dair üst düzey bir açıklama var:
- Etiket ataması
- Bellek ayrıldığında (ör. kernel veya user space içinde secure allocators aracılığıyla), o blok için bir gizli etiket atanır.
- Kullanıcıya veya kernela döndürülen pointer, bu etiketi üst bitlerinde içerir (TBI / top byte ignore mekanizmalarını kullanarak).
- Erişimde etiket kontrolü
- Bir pointer kullanılarak her load veya store çalıştırıldığında, donanım pointer’ın etiketinin bellek bloğunun etiketi (allocation tag) ile eşleşip eşleşmediğini kontrol eder. Uyuşmazlık varsa, hemen fault oluşur (eşzamanlı olduğundan).
- Serbest bırakma / yeniden kullanım sırasında yeniden etiketleme
- Bellek free edildiğinde, allocator bloğun etiketini değiştirir (eski etiketli pointer’lar artık eşleşmez).
- Bu nedenle use-after-free pointer eski bir etiket taşıyacak ve erişildiğinde uyuşmazlık oluşacaktır.
- Taşmaları yakalamak için komşu etiket farklılaştırması
- Komşu allocations’a farklı etiketler atanır. Eğer bir buffer overflow komşu belleğe taşarsa, etiket uyuşmazlığı bir fault tetikler.
- Bu, sınırı geçen küçük overflow’ları yakalamada özellikle güçlüdür.
- Etiket gizliliğinin uygulanması
- Apple, etiket değerlerinin leaked olmasını engellemelidir (çünkü bir attacker etiketleri öğrenirse doğru etiketlere sahip pointer’lar craft edebilir).
- Bunun için microarchitectural / speculative kontroller gibi korumalar içerirler, böylece tag bit’lerinin side-channel leakage’ı önlenir.
- Kernel ve user-space entegrasyonu
- Apple, EMTE’yi sadece user-space’te değil, kernel / OS kritik bileşenlerde de kullanır (kernel’i memory corruption’a karşı korumak için).
- Donanım/OS, kernel user space adına çalışırken bile etiket kurallarının uygulanmasını sağlar.
Örnek
``` Allocate A = 0x1000, assign tag T1 Allocate B = 0x2000, assign tag T2// pointer P points into A with tag T1 P = (T1 << 56) | 0x1000
// Valid store *(P + offset) = value // tag T1 matches allocation → allowed
// Overflow attempt: P’ = P + size_of_A (into B region) *(P’ + delta) = value → pointer includes tag T1 but memory block has tag T2 → mismatch → fault
// Free A, allocator retags it to T3 free(A)
// Use-after-free: *(P) = value → pointer still has old tag T1, memory region is now T3 → mismatch → fault
</details>
#### Sınırlamalar & challenges
- **Intrablock overflows**: Eğer overflow aynı allocation içinde kalır (boundary’i aşmaz) ve tag aynı kalmaya devam ederse, tag mismatch bunu yakalamaz.
- **Tag width limitation**: Tag için yalnızca birkaç bit (ör. 4 bit veya küçük bir domain) mevcut — sınırlı namespace.
- **Side-channel leaks**: Eğer tag bitleri (cache / speculative execution yoluyla) leaked olabiliyorsa, saldırgan geçerli tagleri öğrenip atlatabilir. Apple’ın tag confidentiality enforcement bunu hafifletmeyi amaçlar.
- **Performance overhead**: Her load/store sırasında yapılan tag kontrolleri maliyet katar; Apple donanımı optimize ederek overhead’i düşük tutmak zorunda.
- **Compatibility & fallback**: Eski donanımda veya EMTE desteklemeyen bölümlerde fallback mekanizmaları gerekli. Apple, MIE’nin yalnızca destekleyen cihazlarda etkinleştirildiğini iddia ediyor.
- **Complex allocator logic**: Allocator tag’leri, retagging, hizalama sınırlarını ve yanlış-tag çakışmalarını yönetmek zorunda. Allocator mantığındaki hatalar yeni zafiyetler ortaya çıkarabilir.
- **Mixed memory / hybrid areas**: Bazı bellek bölgeleri untagged (legacy) kalabilir; bu da birlikte çalışmayı daha karmaşık hale getirir.
- **Speculative / transient attacks**: Birçok mikro-mimari korumada olduğu gibi, speculative execution veya micro-op füzyonları kontrolleri transient olarak atlatabilir ya da tag bitlerini leak edebilir.
- **Limited to supported regions**: Apple EMTE’yi yalnızca seçili, yüksek-riskli alanlarda (kernel, security-critical subsystems) zorunlu kılabilir; evrensel olmayabilir.
---
## Key enhancements / differences compared to standard MTE
Here are the improvements and changes Apple emphasizes:
| Feature | Original MTE | EMTE (Apple’s enhanced) / MIE |
|---|---|---|
| **Check mode** | Supports synchronous and asynchronous modes. In async, tag mismatches are reported later (delayed)| Apple insists on **synchronous mode** by default—tag mismatches are caught immediately, no delay/race windows allowed.|
| **Coverage of non-tagged memory** | Accesses to non-tagged memory (e.g. globals) may bypass checks in some implementations | EMTE requires that accesses from a tagged region to non-tagged memory also validate tag knowledge, making it harder to bypass by mixing allocations.|
| **Tag confidentiality / secrecy** | Tags might be observable or leaked via side channels | Apple adds **Tag Confidentiality Enforcement**, which attempts to prevent leakage of tag values (via speculative side-channels etc.).|
| **Allocator integration & retagging** | MTE leaves much of allocator logic to software | Apple’s secure typed allocators (kalloc_type, xzone malloc, etc.) integrate with EMTE: when memory is allocated or freed, tags are managed at fine granularity.|
| **Always-on by default** | In many platforms, MTE is optional or off by default | Apple enables EMTE / MIE by default on supported hardware (e.g. iPhone 17 / A19) for kernel and many user processes.|
Because Apple controls both the hardware and software stack, it can enforce EMTE tightly, avoid performance pitfalls, and close side-channel holes.
---
## How EMTE works in practice (Apple / MIE)
Here’s a higher-level description of how EMTE operates under Apple’s MIE setup:
1. **Tag assignment**
- When memory is allocated (e.g. in kernel or user space via secure allocators), a **secret tag** is assigned to that block.
- The pointer returned to the user or kernel includes that tag in its high bits (using TBI / top byte ignore mechanisms).
2. **Tag checking on access**
- Whenever a load or store is executed using a pointer, the hardware checks that the pointer’s tag matches the memory block’s tag (allocation tag). If mismatch, it faults immediately (since synchronous).
- Because it's synchronous, there is no “delayed detection” window.
3. **Retagging on free / reuse**
- When memory is freed, the allocator changes the block’s tag (so older pointers with old tags no longer match).
- A use-after-free pointer would therefore have a stale tag and mismatch when accessed.
4. **Neighbor-tag differentiation to catch overflows**
- Adjacent allocations are given distinct tags. If a buffer overflow spills into neighbor’s memory, tag mismatch causes a fault.
- This is especially powerful in catching small overflows that cross boundary.
5. **Tag confidentiality enforcement**
- Apple must prevent tag values being leaked (because if attacker learns the tag, they could craft pointers with correct tags).
- They include protections (microarchitectural / speculative controls) to avoid side-channel leakage of tag bits.
6. **Kernel and user-space integration**
- Apple uses EMTE not just in user-space but also in kernel / OS-critical components (to guard kernel against memory corruption).
- The hardware/OS ensures tag rules apply even when kernel is executing on behalf of user space.
Because EMTE is built into MIE, Apple uses EMTE in synchronous mode across key attack surfaces, not as opt-in or debugging mode.
---
## Exception handling in XNU
When an **exception** occurs (e.g., `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, etc.), the **Mach layer** of the XNU kernel is responsible for intercepting it before it becomes a UNIX-style **signal** (like `SIGSEGV`, `SIGBUS`, `SIGILL`, ...).
This process involves multiple layers of exception propagation and handling before reaching user space or being converted to a BSD signal.
### Exception Flow (High-Level)
1. **CPU triggers a synchronous exception** (e.g., invalid pointer dereference, PAC failure, illegal instruction, etc.).
2. **Low-level trap handler** runs (`trap.c`, `exception.c` in XNU source).
3. The trap handler calls **`exception_triage()`**, the core of the Mach exception handling.
4. `exception_triage()` decides how to route the exception:
- First to the **thread's exception port**.
- Then to the **task's exception port**.
- Then to the **host's exception port** (often `launchd` or `ReportCrash`).
If none of these ports handle the exception, the kernel may:
- **Convert it into a BSD signal** (for user-space processes).
- **Panic** (for kernel-space exceptions).
### Core Function: `exception_triage()`
The function `exception_triage()` routes Mach exceptions up the chain of possible handlers until one handles it or until it's finally fatal. It's defined in `osfmk/kern/exception.c`.
```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);
Tipik Çağrı Akışı:
exception_triage() └── exception_deliver() ├── exception_deliver_thread() ├── exception_deliver_task() └── exception_deliver_host()
Hepsi başarısız olursa → bsd_exception() tarafından işlenir → SIGSEGV gibi bir sinyale çevrilir.
Exception Ports
Her Mach nesnesi (thread, task, host) exception ports kaydedebilir; bu portlara istisna mesajları gönderilir.
Bunlar API tarafından tanımlanırlar:
task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()
Her exception port şunlara sahiptir:
- A mask (hangi exception’ları almak istediği)
- A port name (mesajları almak için Mach portu)
- A behavior (kernel’in mesajı nasıl gönderdiği)
- A flavor (hangi thread state’in dahil edileceği)
Debuggers ve Exception Handling
Bir debugger (ör. LLDB), hedef task veya thread üzerinde genellikle task_set_exception_ports() kullanarak bir exception port ayarlar.
Bir exception oluştuğunda:
- Mach mesajı debugger process’ine gönderilir.
- Debugger, exception’ı handle (resume, registerları değiştir, instruction’ı atla) etmeye veya etmemeye karar verebilir.
- Eğer debugger handle etmezse, exception bir sonraki seviyeye yayılır (task → host).
Flow of EXC_BAD_ACCESS
-
Thread geçersiz bir pointer’ı dereference eder → CPU Data Abort yükseltir.
-
Kernel trap handler
exception_triage(EXC_BAD_ACCESS, ...)çağırır. -
Mesaj gönderilir:
-
Thread port → (debugger breakpoint’i intercept edebilir).
-
Eğer debugger yok sayarsa → Task port → (process-seviyesinde handler).
-
Eğer yine yok sayılırsa → Host port (genellikle ReportCrash).
- Kimse handle etmezse →
bsd_exception()bunuSIGSEGV’ye çevirir.
PAC Exceptions
Pointer Authentication (PAC) başarısız olduğunda (signature uyuşmazlığı), özel bir Mach exception yükseltilir:
EXC_ARM_PAC(tip)- Kodlar detay içerebilir (ör. key type, pointer type).
Eğer binary’de TFRO_PAC_EXC_FATAL flag’i varsa, kernel PAC hatalarını fatal olarak ele alır ve debugger intercept’ini atlar. Bu, saldırganların debugger kullanarak PAC kontrollerini atlamasını önlemek içindir ve platform binaries için etkindir.
Software Breakpoints
Bir software breakpoint (int3 on x86, brk on ARM64) kasıtlı bir fault oluşturularak uygulanır.
Debugger bunu exception port üzerinden yakalar:
- Instruction pointer veya belleği değiştirir.
- Orijinal instruction’ı geri yükler.
- Execution’ı resume eder.
Aynı mekanizma bir PAC exception’ı “yakalamanıza” da izin verir — TFRO_PAC_EXC_FATAL set değilse. Eğer flag set ise, debugger’a hiç ulaşmaz.
Conversion to BSD Signals
Eğer hiçbir handler exception’ı kabul etmezse:
-
Kernel
task_exception_notify() → bsd_exception()çağırır. -
Bu, Mach exception’ları signal’lara map eder:
| Mach Exception | Signal |
|---|---|
| EXC_BAD_ACCESS | SIGSEGV or SIGBUS |
| EXC_BAD_INSTRUCTION | SIGILL |
| EXC_ARITHMETIC | SIGFPE |
| EXC_SOFTWARE | SIGTRAP |
| EXC_BREAKPOINT | SIGTRAP |
| EXC_CRASH | SIGKILL |
| EXC_ARM_PAC | SIGILL (on non-fatal) |
Key Files in XNU Source
-
osfmk/kern/exception.c→exception_triage(),exception_deliver_*()’in çekirdeği. -
bsd/kern/kern_sig.c→ Signal teslimat mantığı. -
osfmk/arm64/trap.c→ Düşük seviye trap handler’ları. -
osfmk/mach/exc.h→ Exception kodları ve yapıları. -
osfmk/kern/task.c→ Task exception port kurulumları.
Old Kernel Heap (Pre-iOS 15 / Pre-A12 era)
Kernel, sabit boyutlu “zone“lara bölünmüş bir zone allocator (kalloc) kullanıyordu.
Her zone yalnızca tek bir boyut sınıfındaki allocation’ları tutardı.
Ekran görüntüsünden:
| Zone Name | Element Size | Example Use |
|---|---|---|
default.kalloc.16 | 16 bytes | Çok küçük kernel struct’ları, pointer’lar. |
default.kalloc.32 | 32 bytes | Küçük struct’lar, object header’ları. |
default.kalloc.64 | 64 bytes | IPC mesajları, küçük kernel buffer’ları. |
default.kalloc.128 | 128 bytes | OSObject gibi orta boyutlu objelerin parçaları. |
| … | … | … |
default.kalloc.1280 | 1280 bytes | Büyük yapılar, IOSurface/graphics metadata. |
Nasıl çalışıyordu:
- Her allocation isteği en yakın zone boyutuna yuvarlanır.
(Örn. 50-byte istek
kalloc.64zonuna düşer). - Her zone’daki bellek bir freelist içinde tutulurdu — kernel tarafından freed edilen chunk’lar o zone’a geri giderdi.
- Eğer 64-byte’lık bir buffer’ı overflow ederseniz, aynı zone’daki bir sonraki objeyi overwrite ederdiniz.
Bu yüzden heap spraying / feng shui bu kadar etkiliydi: aynı boyut sınıfında allocation’lar spray yaparak obje komşularını tahmin edebilirdiniz.
The freelist
Her kalloc zone’unun içinde, freed edilmiş objeler doğrudan sisteme geri verilmez — bir freelist’e, kullanılabilir chunk’ların bağlı listesine giderdi.
-
Bir chunk free edildiğinde, kernel o chunk’ın başlangıcına bir pointer yazar → aynı zone’daki bir sonraki free chunk’ın adresi.
-
Zone, ilk free chunk’a işaret eden bir HEAD pointer tutardı.
-
Allocation her zaman mevcut HEAD’i kullanırdı:
-
HEAD’i pop et (o belleği caller’a döndür).
-
HEAD = HEAD->next (freed chunk’ın header’ında saklanan).
-
Free etmek chunk’ları geri iter:
-
freed_chunk->next = HEAD -
HEAD = freed_chunk
Yani freelist, freed bellek içinde inşa edilen basit bir bağlı listedendi.
Normal durum:
Zone page (64-byte chunks for example):
[ A ] [ F ] [ F ] [ A ] [ F ] [ A ] [ F ]
Freelist view:
HEAD ──► [ F ] ──► [ F ] ──► [ F ] ──► [ F ] ──► NULL
(next ptrs stored at start of freed chunks)
freelist’i İstismar Etme
Bir free chunk’ın ilk 8 baytı freelist pointer olduğu için, bir saldırgan bunu bozabilir:
- Heap overflow ile bitişik freed chunk’a → onun “next” pointer’ını üzerine yazma.
- Use-after-free ile freed object içine yazma → onun “next” pointer’ını üzerine yazma.
Sonraki aynı boyuttaki allocation sırasında:
- Allocator bozulmuş chunk’ı poplar.
- Saldırgan tarafından verilen “next” pointer’ını takip eder.
- Rastgele belleğe işaret eden bir pointer döndürür; bu fake object primitives veya hedefe yönelik overwrite’a olanak tanır.
freelist poisoning’in görsel örneği:
Before corruption:
HEAD ──► [ F1 ] ──► [ F2 ] ──► [ F3 ] ──► NULL
After attacker overwrite of F1->next:
HEAD ──► [ F1 ]
(next) ──► 0xDEAD_BEEF_CAFE_BABE (attacker-chosen)
Next alloc of this zone → kernel hands out memory at attacker-controlled address.
This freelist tasarımı, hardening öncesinde exploit’i son derece etkili kılıyordu: heap sprays kaynaklı öngörülebilir komşular, raw pointer freelist bağlantıları ve tür ayrımı olmaması saldırganların UAF/overflow hatalarını rastgele kernel bellek kontrolüne yükseltmesine izin veriyordu.
Heap Grooming / Feng Shui
The goal of heap grooming is to shape the heap layout so that when an attacker triggers an overflow or use-after-free, the target (victim) object sits right next to an attacker-controlled object.
That way, when memory corruption happens, the attacker can reliably overwrite the victim object with controlled data.
Steps:
- Spray allocations (fill the holes)
- Zamanla kernel heap parçalanır: bazı zone’larda eski nesnelerin serbest bırakıldığı boşluklar oluşur.
- Saldırgan önce bu boşlukları doldurmak için çok sayıda dummy allocation oluşturur, böylece heap “packed” ve öngörülebilir hale gelir.
- Force new pages
- Delikler dolduktan sonra, sonraki allocation’lar zone’a eklenen yeni sayfalardan gelmek zorunda kalır.
- Yeni sayfalar demek, nesnelerin eski parçalanmış belleğe dağılmak yerine birlikte kümeleneceği anlamına gelir.
- Bu, saldırganın komşuları çok daha iyi kontrol etmesini sağlar.
- Place attacker objects
- Saldırgan şimdi tekrar spray yapar ve bu yeni sayfalarda çok sayıda saldırgan-kontrollü nesne oluşturur.
- Bu nesnelerin boyutu ve yerleşimi öngörülebilirdir (hepsi aynı zone’a ait oldukları için).
- Free a controlled object (make a gap)
- Saldırgan kasıtlı olarak kendi nesnelerinden birini free eder.
- Bu, allocator’ın daha sonra aynı boyuttaki bir allocation için tekrar kullanacağı bir “delik” oluşturur.
- Victim object lands in the hole
- Saldırgan, kernel’in hedef (victim) nesneyi (bozmak istediği) allocate etmesini tetikler.
- Delik freelist’teki ilk uygun slot olduğundan, victim nesne tam olarak saldırganın boşalttığı yere yerleştirilir.
- Overflow / UAF into victim
- Artık saldırgan, victim’in çevresinde saldırgan-kontrollü nesnelere sahiptir.
- Kendi nesnelerinden birinden overflow yaparak (veya freed olanı yeniden kullanarak), victim’in bellek alanlarını seçilmiş değerlerle güvenilir şekilde overwrite edebilir.
Neden işe yarıyor:
- Zone allocator öngörülebilirliği: aynı boyuttaki allocation’lar her zaman aynı zone’dan gelir.
- Freelist davranışı: yeni allocation’lar en son free edilen chunk’ı önce yeniden kullanır.
- Heap sprays: saldırgan belleği öngörülebilir içerikle doldurur ve yerleşimi kontrol eder.
- Sonuç: saldırgan victim nesnenin nerede yerleşeceğini ve yanında ne tür verilerin olacağını kontrol eder.
Modern Kernel Heap (iOS 15+/A12+ SoCs)
Apple allocator’u sertleştirdi ve heap grooming çok daha zor hale geldi:
1. From Classic kalloc to kalloc_type
- Before: her boyut sınıfı için tek bir
kalloc.<size>zone’u vardı (16, 32, 64, … 1280, vb.). O boyuttaki herhangi bir nesne oraya yerleştirilirdi → saldırgan nesneleri yetkili kernel nesnelerinin yanına gelebiliyordu. - Now:
- Kernel nesneleri typed zones (
kalloc_type) üzerinden allocate ediliyor. - Her nesne türünün (örn.
ipc_port_t,task_t,OSString,OSData) kendi adanmış zone’u var, aynı boyutta olsalar bile. - Nesne türü ↔ zone eşlemesi compile zamanında kalloc_type system tarafından üretilir.
Artık saldırgan controlled veri (OSData) ile hassas kernel nesneleri (task_t) aynı boyutta olsa bile yan yana geleceğini garanti edemez.
2. Slabs and Per-CPU Caches
- Heap, slablara bölünmüştür (o zone için sabit boyutlu chunk’lara bölünmüş sayfalar).
- Her zone, contention’u azaltmak için bir per-CPU cache içerir.
- Allocation yolu:
- Per-CPU cache denenir.
- Boşsa, global freelist’ten çekilir.
- Freelist boşsa, yeni bir slab (bir veya daha fazla sayfa) allocate edilir.
- Avantaj: Bu merkezsizleştirme, allocation’ların farklı CPU’ların cache’lerinden karşılanabilmesi nedeniyle heap spray’leri daha az deterministik kılar.
3. Randomization inside zones
- Bir zone içinde, freed elemanlar basit FIFO/LIFO sırasıyla geri verilmez.
- Modern XNU, encoded freelist pointers (Linux benzeri safe-linking, yaklaşık iOS 14’ten itibaren) kullanır.
- Her freelist pointer bir per-zone gizli cookie ile XOR-encoded edilir.
- Bu, saldırganların bir write primitive kazandıklarında sahte freelist pointer’ı oluşturmasını engeller.
- Bazı allocation’ların slab içinde yerleşimi randomize edilir, bu yüzden spraying adjacency’yi garanti etmez.
4. Guarded Allocations
- Bazı kritik kernel nesneleri (örn. credentials, task yapıları) guarded zones içinde allocate edilir.
- Bu zone’lar slab’lar arasında guard pages (unmapped memory) ekler veya nesnelerin etrafına redzones koyar.
- Guard page’e yapılan herhangi bir overflow bir fault tetikler → sessiz bozulma yerine hemen panic.
5. Page Protection Layer (PPL) and SPTM
- Freed bir nesneyi kontrol etseniz bile kernel belleğinin her yerini değiştiremezsiniz:
- PPL (Page Protection Layer), belirli bölgelerin (örn. code signing verileri, entitlements) kernel için bile read-only olmasını zorunlu kılar.
- A15/M2+ cihazlarda, bu rol SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor) ile değiştirilmiş/geliştirilmiştir.
- Bu donanım tarafından uygulanmış katmanlar, saldırganların tek bir heap corruption’dan kritik güvenlik yapılarını keyfi olarak patch’lemeye yükseltmesini engeller.
- (Added / Enhanced): ayrıca kernel pointer’ları (özellikle function pointer, vtable) korumak için PAC (Pointer Authentication Codes) kernel içinde kullanılır; bu da onları sahteleştirmeyi veya bozmayı zorlaştırır.
- (Added / Enhanced): zone’lar zone_require / zone enforcement uygulayabilir; yani bir nesnenin free edilmesi sadece doğru typed zone üzerinden geri döndürülebilir; geçersiz cross-zone free’lar panic’e veya reddedilmeye yol açabilir. (Apple bu konudan memory safety yazılarında bahseder)
6. Large Allocations
- Tüm allocation’lar
kalloc_typeüzerinden gitmez. - Çok büyük istekler (~16 KB üzeri) typed zone’ları atlar ve sayfa allocation’ları yoluyla doğrudan kernel VM (kmem) üzerinden servis edilir.
- Bunlar daha az öngörülebilirdir, ama aynı zamanda daha az exploitable’dır çünkü diğer nesnelerle slab paylaşmazlar.
7. Allocation Patterns Attackers Target
Bu korumalara rağmen saldırganlar hâlâ şunları ararlar:
- Reference count objects: retain/release sayaçlarını bozabiliyorsanız use-after-free oluşturabilirsiniz.
- Objects with function pointers (vtables): bunları bozmak hâlâ kontrol akışı sağlayabilir.
- Shared memory objects (IOSurface, Mach ports): bunlar user ↔ kernel köprüsü oldukları için hâlâ hedeflerdir.
Ama — eskisi gibi — sadece OSData spray yapıp bir task_t yanında olmasını bekleyemezsiniz. Başarılı olmak için type-specific bugs veya info leaks gerekir.
Example: Allocation Flow in Modern Heap
Suppose userspace calls into IOKit to allocate an OSData object:
- Type lookup →
OSDatamaps tokalloc_type_osdatazone (size 64 bytes). - Check per-CPU cache for free elements.
- If found → return one.
- If empty → go to global freelist.
- If freelist empty → allocate a new slab (page of 4KB → 64 chunks of 64 bytes).
- Return chunk to caller.
Freelist pointer protection:
- Each freed chunk stores the address of the next free chunk, but encoded with a secret key.
- Overwriting that field with attacker data won’t work unless you know the key.
Comparison Table
| Feature | Old Heap (Pre-iOS 15) | Modern Heap (iOS 15+ / A12+) |
|---|---|---|
| Allocation granularity | Fixed size buckets (kalloc.16, kalloc.32, etc.) | Size + type-based buckets (kalloc_type) |
| Placement predictability | High (same-size objects side by side) | Low (same-type grouping + randomness) |
| Freelist management | Raw pointers in freed chunks (easy to corrupt) | Encoded pointers (safe-linking style) |
| Adjacent object control | Easy via sprays/frees (feng shui predictable) | Hard — typed zones separate attacker objects |
| Kernel data/code protections | Few hardware protections | PPL / SPTM protect page tables & code pages, and PAC protects pointers |
| Allocation reuse validation | None (freelist pointers raw) | zone_require / zone enforcement |
| Exploit reliability | High with heap sprays | Much lower, requires logic bugs or info leaks |
| Large allocations handling | All small allocations managed equally | Large ones bypass zones → handled via VM |
Modern Userland Heap (iOS, macOS — type-aware / xzone malloc)
In recent Apple OS versions (especially iOS 17+), Apple introduced a more secure userland allocator, xzone malloc (XZM). This is the user-space analog to the kernel’s kalloc_type, applying type awareness, metadata isolation, and memory tagging safeguards.
Goals & Design Principles
- Type segregation / type awareness: group allocations by type or usage (pointer vs data) to prevent type confusion and cross-type reuse.
- Metadata isolation: separate heap metadata (e.g. free lists, size/state bits) from object payloads so that out-of-bounds writes are less likely to corrupt metadata.
- Guard pages / redzones: insert unmapped pages or padding around allocations to catch overflows.
- Memory tagging (EMTE / MIE): work in conjunction with hardware tagging to detect use-after-free, out-of-bounds, and invalid accesses.
- Scalable performance: maintain low overhead, avoid excessive fragmentation, and support many allocations per second with low latency.
Architecture & Components
Below are the main elements in the xzone allocator:
Segment Groups & Zones
- Segment groups partition the address space by usage categories: e.g.
data,pointer_xzones,data_large,pointer_large. - Each segment group contains segments (VM ranges) that host allocations for that category.
- Associated with each segment is a metadata slab (separate VM area) that stores metadata (e.g. free/used bits, size classes) for that segment. This out-of-line (OOL) metadata ensures that metadata is not intermingled with object payloads, mitigating corruption from overflows.
- Segments are carved into chunks (slices) which in turn are subdivided into blocks (allocation units). A chunk is tied to a specific size class and segment group (i.e. all blocks in a chunk share the same size & category).
- For small / medium allocations, it will use fixed-size chunks; for large/huges, it may map separately.
Chunks & Blocks
- A chunk is a region (often several pages) dedicated to allocations of one size class within a group.
- Inside a chunk, blocks are slots available for allocations. Freed blocks are tracked via the metadata slab — e.g. via bitmaps or free lists stored out-of-line.
- Between chunks (or within), guard slices / guard pages may be inserted (e.g. unmapped slices) to catch out-of-bounds writes.
Type / Type ID
- Every allocation site (or call to malloc, calloc, etc.) is associated with a type identifier (a
malloc_type_id_t) which encodes what kind of object is being allocated. That type ID is passed to the allocator, which uses it to select which zone / segment to serve the allocation. - Because of this, even if two allocations have the same size, they may go into entirely different zones if their types differ.
- In early iOS 17 versions, not all APIs (e.g. CFAllocator) were fully type-aware; Apple addressed some of those weaknesses in iOS 18.
Allocation & Freeing Workflow
Here is a high-level flow of how allocation and deallocation operate in xzone:
- malloc / calloc / realloc / typed alloc is invoked with a size and type ID.
- The allocator uses the type ID to pick the correct segment group / zone.
- Within that zone/segment, it seeks a chunk that has free blocks of the requested size.
- It may consult local caches / per-thread pools or free block lists from metadata.
- If no free block is available, it may allocate a new chunk in that zone.
- The metadata slab is updated (free bit cleared, bookkeeping).
- If memory tagging (EMTE) is in play, the returned block gets a tag assigned, and metadata is updated to reflect its “live” state.
- When
free()is called:
- The block is marked as freed in metadata (via OOL slab).
- The block may be placed into a free list or pooled for reuse.
- Optionally, block contents may be cleared or poisoned to reduce data leaks or use-after-free exploitation.
- The hardware tag associated with the block may be invalidated or re-tagged.
- If an entire chunk becomes free (all blocks freed), the allocator may reclaim that chunk (unmap it or return to OS) under memory pressure.
Security Features & Hardening
These are the defenses built into modern userland xzone:
| Feature | Purpose | Notes |
|---|---|---|
| Metadata decoupling | Prevent overflow from corrupting metadata | Metadata lives in separate VM region (metadata slab) |
| Guard pages / unmapped slices | Catch out-of-bounds writes | Helps detect buffer overflows rather than silently corrupting adjacent blocks |
| Type-based segregation | Prevent cross-type reuse & type confusion | Even same-size allocations from different types go to different zones |
| Memory Tagging (EMTE / MIE) | Detect invalid access, stale references, OOB, UAF | xzone works in concert with hardware EMTE in synchronous mode (“Memory Integrity Enforcement”) |
| Delayed reuse / poisoning / zap | Reduce chance of use-after-free exploitation | Freed blocks may be poisoned, zeroed, or quarantined before reuse |
| Chunk reclamation / dynamic unmapping | Reduce memory waste and fragmentation | Entire chunks may be unmapped when unused |
| Randomization / placement variation | Prevent deterministic adjacency | Blocks in a chunk and chunk selection may have randomized aspects |
| Segregation of “data-only” allocations | Separate allocations that don’t store pointers | Reduces attacker control over metadata or control fields |
Interaction with Memory Integrity Enforcement (MIE / EMTE)
- Apple’s MIE (Memory Integrity Enforcement) is the hardware + OS framework that brings Enhanced Memory Tagging Extension (EMTE) into always-on, synchronous mode across major attack surfaces.
- xzone allocator is a fundamental foundation of MIE in user space: allocations done via xzone get tags, and accesses are checked by hardware.
- In MIE, the allocator, tag assignment, metadata management, and tag confidentiality enforcement are integrated to ensure that memory errors (e.g. stale reads, OOB, UAF) are caught immediately, not exploited later.
- If you like, I can also generate a cheat-sheet or diagram of xzone internals for your book. Do you want me to do that next?
- :contentReference[oai:20]{index=20}
(Old) Physical Use-After-Free via IOSurface
Ghidra Install BinDiff
Download BinDiff DMG from https://www.zynamics.com/bindiff/manual and install it.
Open Ghidra with ghidraRun and go to File –> Install Extensions, press the add button and select the path /Applications/BinDiff/Extra/Ghidra/BinExport and click OK and isntall it even if there is a version mismatch.
Using BinDiff with Kernel versions
- Go to the page https://ipsw.me/ and download the iOS versions you want to diff. These will be
.ipswfiles. - Decompress until you get the bin format of the kernelcache of both
.ipswfiles. You have information on how to do this on:
macOS Kernel Extensions & Kernelcache
- Open Ghidra with
ghidraRun, create a new project and load the kernelcaches. - Open each kernelcache so they are automatically analyzed by Ghidra.
- Then, on the project Window of Ghidra, right click each kernelcache, select
Export, select formatBinary BinExport (v2) for BinDiffand export them. - Open BinDiff, create a new workspace and add a new diff indicating as primary file the kernelcache that contains the vulnerability and as secondary file the patched kernelcache.
Finding the right XNU version
If you want to check for vulnerabilities in a specific version of iOS, you can check which XNU release version the iOS version uses at [https://www.theiphonewiki.com/wiki/kernel]https://www.theiphonewiki.com/wiki/kernel).
For example, the versions 15.1 RC, 15.1 and 15.1.1 use the version Darwin Kernel Version 21.1.0: Wed Oct 13 19:14:48 PDT 2021; root:xnu-8019.43.1~1/RELEASE_ARM64_T8006.
JSKit-Based Safari Chains and PREYHUNTER Stagers
Renderer RCE abstraction with JSKit
- Reusable entry: Recent in-the-wild chains abused a WebKit JIT bug (patched as CVE-2023-41993) purely to gain JavaScript-level arbitrary read/write. The exploit immediately pivots into a purchased framework called JSKit, so any future Safari bug only needs to deliver the same primitive.
- Version abstraction & PAC bypasses: JSKit bundles support for a wide range of iOS releases together with multiple, selectable Pointer Authentication Code bypass modules. The framework fingerprints the target build, selects the appropriate PAC bypass logic, and verifies every step (primitive validation, shellcode launch) before progressing.
- Manual Mach-O mapping: JSKit parses Mach-O headers directly from memory, resolves the symbols it needs inside dyld-cached images, and can manually map additional Mach-O payloads without writing them to disk. This keeps the renderer process in-memory only and evades code-signature checks tied to filesystem artifacts.
- Portfolio model: Debug strings such as “exploit number 7” show that the suppliers maintain multiple interchangeable WebKit exploits. Once the JS primitive matches JSKit’s interface, the rest of the chain is unchanged across campaigns.
Kernel bridge: IPC UAF -> code-sign bypass pattern
- Kernel IPC UAF (CVE-2023-41992): The second stage, still running inside the Safari context, triggers a kernel use-after-free in IPC code, re-allocates the freed object from userland, and abuses the dangling pointers to pivot into arbitrary kernel read/write. The stage also reuses PAC bypass material previously computed by JSKit instead of re-deriving it.
- Code-signing bypass (CVE-2023-41991): With kernel R/W available, the exploit patches the trust cache / code-signing structures so unsigned payloads execute as
system. The stage then exposes a lightweight kernel R/W service to later payloads. - Composed pattern: This chain demonstrates a reusable recipe that defenders should expect going forward:
WebKit renderer RCE -> kernel IPC UAF -> kernel arbitrary R/W -> code-sign bypass -> unsigned system stager
PREYHUNTER helper & watcher modülleri
- Watcher anti-analysis: Adanmış bir watcher binary cihazı sürekli profillendirir ve bir araştırma ortamı tespit edildiğinde kill-chain’i sonlandırır.
security.mac.amfi.developer_mode_status’ı, birdiagnosticdconsole varlığını,USveyaILlokallerini, Cydia gibi jailbreak izlerini,bash,tcpdump,frida,sshdveyacheckraingibi süreçleri, mobil AV uygulamalarını (McAfee, AvastMobileSecurity, NortonMobileSecurity), özel HTTP proxy ayarlarını ve özel root CA’ları inceler. Herhangi bir kontrol başarısız olursa daha fazla payload teslimatı engellenir. - Helper surveillance hooks: Helper bileşeni diğer aşamalarla
/tmp/helper.socküzerinden iletişim kurar, ardından DMHooker ve UMHooker adlı hook setlerini yükler. Bu hook’lar VOIP audio yollarına erişir (kayıtlar/private/var/tmp/l/voip_%lu_%u_PART.m4aaltında yer alır), sistem çapında bir keylogger uygular, UI olmadan fotoğraf çeker ve SpringBoard’u bu eylemlerin normalde tetikleyeceği bildirimleri bastıracak şekilde hook’lar. Bu nedenle helper, Predator gibi daha ağır implantlar indirilmeden önce gizli bir doğrulama + hafif gözetim katmanı olarak görev yapar.
iMessage/Media Parser Zero-Click Chains
Imessage Media Parser Zero Click Coreaudio Pac Bypass
Referanslar
Tip
AWS Hacking’i öğrenin ve pratik yapın:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın:HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking’i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter’da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
HackTricks

