USMA (User-Space Mapping Attack)¶
pg_vec'i corrupt ederek bir heap UAF/OOB/double-free'yi arbitrary read/write'a çevir; böylecepacket_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_vecpointer'ı ring'in kendi allocation'ının dışına çözülen birAF_PACKETring'i, ya da kernel.textveya page-table page'leriyle örtüşen bir page'i map'leyen birvm_insert_page()anormaldir.- Davranışsal: aynı slab üzerinde heap-spray syscall'larıyla iç içe geçmiş yoğun
PACKET_RX_RINGkurulumu, ardından socket fd'sininmmap()'i.
Mitigation¶
CAP_NET_RAW/ unprivileged user namespace'leri kısıtla (AF_PACKETring 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 veinit_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¶
- Yong Liu, Xiaodong Wang, Jun Yao — USMA: Share Kernel Code with Me (Black Hat Asia 2022), whitepaper — https://i.blackhat.com/Asia-22/Thursday-Materials/AS-22-YongLiu-USMA-Share-Kernel-Code-wp.pdf
- Slides — https://i.blackhat.com/Asia-22/Thursday-Materials/AS-22-YongLiu-USMA-Share-Kernel-Code.pdf