Skip to content

USMA (User-Space Mapping Attack)

pg_vec'i corrupt ederek bir heap UAF/OOB/double-free'yi arbitrary read/write'a çevir; böylece packet_mmap(), attacker-chosen kernel page'lerini (kernel .text, page table'lar) doğrudan userspace'e map'ler.

Mechanism

Neden çalışır

AF_PACKET socket'leri memory-mapped bir ring buffer'ı (PACKET_RX_RING / PACKET_TX_RING) destekler. Böyle bir ring'i setsockopt() ettiğinde kernel bir pg_vec allocate eder — her biri ring'i destekleyen page'lerin bir bloğunu gösteren bir kernel pointer dizisi. Daha sonra socket fd'si üzerinde mmap(), packet_mmap()'i çağırır; bu da pg_vec'i dolaşır, depolanmış her pointer'ı kendi struct page'ine çevirir ve o physical page'i çağıran process'in adres uzayına map'lemesi için vm_insert_page()'e verir.

Güven varsayımı şudur: pg_vec'teki her entry, kernel'in ring için allocate ettiği bir belleği gösterir. USMA tam da bu varsayımı kırar: bir memory corruption bug'ı bir pg_vec entry'sini overwrite etmene izin verirse, onu herhangi bir kernel virtual address'ini gösterecek hale getirebilirsin. packet_mmap() sonra itaatkârca o page'i userspace'e map'ler ve sana arbitrary kernel memory üzerine bir userspace penceresi verir. İstismar edilen invariant: pg_vec entry'lerinin kernel-owned ring page'leri olduğu varsayılır ama vm_insert_page() öncesinde hiçbir şey onları yeniden doğrulamaz.

Bu bir universal converter'dır: "bir kernel pointer'ı corrupt edebiliyorum"u (bir UAF, OOB write ya da double-free'nin çıktısı) "userspace'ten arbitrary kernel memory okuyup yazabiliyorum"a çevirir — kernel'in kendi code'unu ya da page table'larını yeniden map'lemek dahil — ve her zamanki KASLR-leak + ROP gereksinimlerini atlar.

Walkthrough

Yalnızca yetkili test

AF_PACKET ring kurulumu CAP_NET_RAW gerektirir; bir user namespace'te unprivileged bir kullanıcı bunu özel bir net ns üzerinde tutabilir. Yalnızca sahip olduğun bir VM üzerinde test et.

1. Bir packet ring kur (pg_vec allocate eder). Bu kısım zararsızdır:

int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
int ver = TPACKET_V2;
setsockopt(s, SOL_PACKET, PACKET_VERSION, &ver, sizeof ver);

struct tpacket_req req = {
    .tp_block_size = 0x1000, .tp_block_nr = 1,
    .tp_frame_size = 0x1000, .tp_frame_nr = 1,
};
setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof req);
// kernel now holds pg_vec[0] -> a kernel page it allocated for this ring

2. Bug'ınla bir pg_vec entry'sini corrupt et. UAF/OOB/double-free'yi kullanarak pg_vec[0]'ı overwrite et ki açığa çıkarmak istediğin kernel virtual address'ini göstersin. İki yüksek değerli hedef:

# (a) kernel .text  -> remap a code page, patch an instruction to flip creds
# (b) a PTE/PMD page -> a Dirty-Pagetable-style data-only write primitive
pg_vec[0] = (void *) target_kva;   // written via the corruption primitive

Tam corruption bug'ına bağlıdır: pg_vec dizilerini victim cache'e spray'le, victim'i free et ve slot'unu reclaim et ki kontrollü write'ın bir pg_vec pointer'ına insin.

3. Hedefi userspace'e map'le. Socket fd'sini mmap() et; packet_mmap(), sahte pointer'ını vm_insert_page()'e besler:

char *kpage = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE,
                   MAP_SHARED, s, 0);
// kpage[] now aliases target_kva: read = kernel read, write = kernel write

Beklenen etki: kpage, arbitrary bir kernel page'inin userspace alias'ıdır. Onu okumak kernel memory'yi sızdırır (bilinen bir yapıyı yeniden map'lersen ayrı bir KASLR oracle gerekmez); ona yazmak kernel memory'yi doğrudan değiştirir — örn. bir check'i etkisiz kılmak için bir .text byte'ını overwrite et, ya da physical R/W kazanmak için bir page table'ı düzenle ve sonra root için kendi cred'ini patch et.

Neden 'universal'

USMA, tek pointer overwrite'ı nasıl elde ettiğinle ilgilenmez. UAF, OOB write ve double-free hepsi "bir pg_vec entry'sini kontrol et"e indirgenir; ardından read/write primitive'i aynıdır — bu da onu birçok CVE boyunca (örn. CVE-2021-22600, CVE-2022-34918) genel bir exploitation backend'i yapan şeydir.

Detection

  • pg_vec pointer'ı ring'in kendi allocation'ının dışına çözülen bir AF_PACKET ring'i, ya da kernel .text veya page-table page'leriyle örtüşen bir page'i map'leyen bir vm_insert_page() anormaldir.
  • Davranışsal: aynı slab üzerinde heap-spray syscall'larıyla iç içe geçmiş yoğun PACKET_RX_RING kurulumu, ardından socket fd'sinin mmap()'i.

Mitigation

  • CAP_NET_RAW / unprivileged user namespace'leri kısıtla (AF_PACKET ring buna ihtiyaç duyar) — unprivileged saldırganlar için primitive'i kaldırır.
  • Altta yatan corruption'ı engelleyen genel slab hardening: freelist randomization/hardening, CONFIG_SLAB_FREELIST_HARDENED, allocator integrity check'leri ve init_on_free, pg_vec'i reclaim etme şansını azaltır.
  • Kernel .text'i read-only işaretle (CONFIG_STRICT_KERNEL_RWX) ki .text-remap varyantı yazamasın, böylece saldırganı daha zor olan page-table rotasına itersin.

References