Arbitrary physical mapping primitive (One Byte / dart)¶
Bir
vm_map_copytype discriminator'ının one-byte heap corruption'ı, XNU kernel'inin attacker'ın kontrol ettiği inline data'yı sahtevm_map_entryobjelerinden oluşan bir linked list olarak yorumlamasına yol açar; nihayetinde attacker'ın verdiği bir physical page number ilepmap_enter_options()çağrılır ve exploit process'i içinde keyfi bir physical-to-virtual mapping üretilir.
Mechanism¶
Note
XNU'nun vm_map_copy yapısı, offset 0x00'da union gövdesinin üç yorumundan birini seçen bir type field'ı taşır:
| Değer | Anlamı | Union yorumu |
|---|---|---|
| 1 | ENTRY_LIST |
vm_map_entry objelerinden oluşan linked list |
| 2 | OBJECT |
tek bir vm_object_t reference'ı |
| 3 | KERNEL_BUFFER |
inline byte dizisi (kdata[]) |
Type'ı 3 → 1 değiştiren bir one-byte overflow, vm_map_copyout_internal()'in inline kdata'yı gerçek vm_map_entry objelerinin doubly-linked list'iymiş gibi gezmesine yol açar. Bir sahte entry'nin wired_count != 0 olduğunda kernel vm_fault_enter() → pmap_enter_options() çağırır. ARM'de translation table entry yazılmadan önce, verilen physical page number üzerinde başka bir validation yapılmaz (pmap.c kaynağı, Brandon Azad tarafından doğrulandı). Sonuç: attacker'ın adlandırdığı herhangi bir physical page (PPL-korumalı, bir page-table page'i ya da bir DART I/O adresi olmayan), attacker'ın seçtiği protection'larla user-kontrollü bir virtual adrese map'lenir — temiz, keyfi bir physical read/write primitive'i.
Oradan exploit, kernelcache image'ına ait physical page'leri map'ler. Bilinen physical adres 0x200000680'deki AMCC (Apple Memory Controller) register'ı RORGNBASEADDR, kernelcache'in physical base'ini leak eder ve physical space'te KASLR'ı bypass eder. Attacker sonra sysctl_oid yapılarını map'ler, oid_arg1 pointer'larını yeniden yönlendirir ve keyfi virtual kernel read ve write'ları sentezlemek için sysctl node {0,3} (name2mib)'i kötüye kullanır.
Güvenlik açığı CVE-2020-3837 idi (oob_timestamp; komşu bir vm_map_copy'ye ilk one-byte overflow fırsatını veren ayrı bir heap overflow). Physical-mapping tekniğinin kendisi generic'tir ve one-byte corruption'ın nasıl iletildiğinden bağımsızdır.
Walkthrough¶
Brandon Azad'ın Project Zero yazısı "One Byte to rule them all"a (Temmuz 2020) dayanır; iOS 13.3 / A13 hedeflenir.
Step 1 — one-byte vm_map_copy type corruption elde et
oob_timestamp güvenlik açığı (CVE-2020-3837), bir vm_map_copy objesine komşu bir heap allocation üzerinde off-by-one bir write tetikler. İlk byte — type field'ı — 3 (KERNEL_BUFFER)'dan 1 (ENTRY_LIST)'e değiştirilir:
Before: vm_map_copy { type=3, offset=X, size=Y, kdata=[...attacker bytes...] }
After: vm_map_copy { type=1, offset=X, size=Y, hdr.links=<fake vm_map_entry ptr> }
^^^^ changed by 1-byte overflow
Step 2 — Kontrol edilen memory'de sahte vm_map_entry chain'i craft et
kdata bölgesi (artık ENTRY_LIST olarak yorumlanıyor) sahte vm_map_entry struct'larıyla doldurulur. Önemli field'lar:
struct fake_vm_map_entry {
// vm_map_links (must form valid sentinel)
struct vm_map_entry *vme_prev;
struct vm_map_entry *vme_next; // points to sentinel to end list
vm_map_offset_t vme_start; // target VA in exploit process
vm_map_offset_t vme_end;
// ... padding ...
uint16_t wired_count; // must be != 0 to enter fault path
// VME_OBJECT / VME_OFFSET macros read object/offset from bitfield
// Embedded vm_object_t → vm_page_t chain:
// vm_page_lookup(object, offset) must return a fake vm_page
// whose phys_page field = TARGET_PHYS_PAGE_NUMBER
};
Step 3 — Corrupt edilen vm_map_copy'yi OOL memory descriptor ile ilet
Corrupt edilen vm_map_copy, attacker'ın kontrol ettiği bir receive port'una giden bir Mach message'ı içinde out-of-line bir memory descriptor olarak gönderilir:
mach_msg_ool_descriptor_t ool = {
.address = corrupted_copy,
.size = sizeof(corrupted_copy),
.deallocate = FALSE,
.copy = MACH_MSG_VIRTUAL_COPY,
.type = MACH_MSG_OOL_DESCRIPTOR,
};
// Sending this message causes kernel to call vm_map_copyout_internal()
// with our corrupted vm_map_copy, traversing the fake entry list.
Step 4 — pmap_enter_options() keyfi physical page'i map'ler
vm_map_copyout_internal içinde, wired_count != 0 olan her entry için:
// (simplified from osfmk/vm/vm_map.c)
m = vm_page_lookup(VME_OBJECT(entry), VME_OFFSET(entry));
// m->phys_page == TARGET_PHYS_PAGE_NUMBER (attacker-controlled)
vm_fault_enter(m, dst_map->pmap, target_va, VM_PROT_READ|VM_PROT_WRITE, ...);
// -> pmap_enter_options(pmap, target_va, TARGET_PHYS_PAGE_NUMBER, ...)
// ARM pmap.c: no further validation of phys_page before TTE write
Physical page TARGET_PHYS_PAGE_NUMBER artık userspace'te target_va'da erişilebilir.
Step 5 — AMCC register üzerinden kernelcache'i bul
AMCC register page'ini map'le (physical adres 0x200000680):
uint64_t amcc_phys = 0x200000680ULL;
uint64_t amcc_va = map_phys_page(amcc_phys); // using primitive above
uint64_t kc_phys_base = *(uint64_t*)(amcc_va + RORGNBASEADDR_OFFSET);
// kc_phys_base = start of lockdown region containing kernelcache
Physical space'te KASLR, A13'te yalnızca ~32 MB entropy'dir; kc_phys_base'den tarama yapmak Mach-O header'ını bulur.
Step 6 — sysctl hijack ile virtual kernel R/W sentezle
Kernelcache page'lerini map'le, sysctl_oid node'larını bul:
// Overwrite oid_arg1 of a sandbox-accessible sysctl node
// to point to target kernel address.
// Read via: sysctl(OID_PATH, NULL, &buf, &len, NULL, 0)
// Write via: sysctl node {0,3} (sysctl.name2mib) undocumented behaviour
Beklenen sonuç: caller bir kernel_read64(addr) / kernel_write64(addr, val) fonksiyon çifti kazanır.
Detection¶
- Beklenmedik bir physical adrese erişen
vm_map_copyout_internal→vm_fault_enterüzerinden geçen bir backtrace ile kernel panic. - Sandbox'lı bir process'ten OOL descriptor'lar içeren ve ardından physical-range memory'ye erişimin geldiği anormal
mach_msgpaternleri. - Attacker page-table page'leri map'lemeye çalışırsa PPL violation log'ları (PPL ARM'de panic'ler).
Mitigation¶
- PPL (Page Protection Layer): kernelcache ve page-table physical page'lerinin map'lenmesini engeller; primitive'in etkisini sınırlar ama ortadan kaldırmaz.
- zone_require:
vm_map_copyallocation'larının doğru zone'dan kaynaklanmasını zorlamak, type-confusion'ı daha erken tespit ederdi. pmap-io-rangesdevicetree property'sindeki DART I/O protection flag'leri, IOMMU register page'lerinin map'lenmesini engeller.- Fix: dispatch'ten önce
vm_map_copyout_internal'dakitypefield'ını doğrula; ENTRY_LIST traversal'ında size/bounds check'leri ekle.