Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)
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
```c 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();
</details>
## 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)](https://xploitbengineer.github.io/CVE-2025-21479)
- [Mesa freedreno afuc disassembler README (microcode + packets)](https://gitlab.freedesktop.org/mesa/mesa/-/blob/c0f56fc64cad946d5c4fda509ef3056994c183d9/src/freedreno/afuc/README.rst)
- [Google Project Zero: Attacking Qualcomm Adreno GPU (SMMU takeover via CP packets)](https://googleprojectzero.blogspot.com/2020/09/attacking-qualcomm-adreno-gpu.html)
- [Dirty pagetable (archive)](https://web.archive.org/web/20240425043203/https://yanglingxi1993.github.io/dirty_pagetable/dirty_pagetable.html)
> [!TIP]
> Ucz się i ćwicz Hacking AWS:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> Ucz się i ćwicz Hacking GCP: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Ucz się i ćwicz Hacking Azure: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>Wsparcie dla HackTricks</summary>
>
> - Sprawdź [**plany subskrypcyjne**](https://github.com/sponsors/carlospolop)!
> - **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Dziel się trikami hackingowymi, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repozytoriów na githubie.
>
> </details>
HackTricks

