Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Hierdie bladsy abstraheer ’n in-die-wild Adreno A7xx microcode logika fout (CVE-2025-21479) in reproduceerbare exploitation-tegnieke: misbruik van IB-vlak masking in Set Draw State (SDS) om bevoorregte GPU-pakkette van ’n nie-bevoorregte app uit te voer, skakel na GPU SMMU takeover en daarna na ’n vinnige, stabiele kernel R/W via ’n dirty-pagetable trick.
- Affected: Qualcomm Adreno A7xx GPU firmware prior to a microcode fix that changed masking of register $12 from 0x3 to 0x7.
- Primitive: Execute privileged CP packets (e.g., CP_SMMU_TABLE_UPDATE) from SDS, which is user-controlled.
- Outcome: Arbitrary physical/virtual kernel memory R/W, SELinux disable, root.
- Prereq: Ability to create a KGSL GPU context and submit command buffers that enter SDS (normal app capability).
Background: IB levels, SDS and the $12 mask
- The kernel maintains a ringbuffer (RB=IB0). Userspace submits IB1 via CP_INDIRECT_BUFFER, chaining to IB2/IB3.
- SDS is a special command stream entered via CP_SET_DRAW_STATE:
- A6xx: SDS is treated as IB3
- A7xx: SDS moved to IB4
- Microcode tracks the current IB level in register $12 and gates privileged packets so they are only accepted when the effective level corresponds to IB0 (kernel RB).
- Bug: A7xx microcode kept masking $12 with 0x3 (2 bits) instead of 0x7 (3 bits). Since IB4 & 0x3 == 0, SDS was misidentified as IB0, allowing privileged packets from user-controlled SDS.
Waarom dit saak maak:
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
Microcode diff-voorbeeld (patch het die masker na 0x7 geskuif):
@@ 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
Exploitation overview
Goal: From SDS (misread as IB0) issue privileged CP packets to re-point the GPU SMMU to attacker-crafted page tables, then use GPU copy/write packets for arbitrary physical R/W. Finally, pivot to a fast CPU-side R/W via dirty pagetable.
Hoëvlak-ketting
- Maak ’n vals GPU pagetable in gedeelde geheue
- Betree SDS en voer uit:
- CP_SMMU_TABLE_UPDATE -> skakel na die vals pagetable
- CP_MEM_WRITE / CP_MEM_TO_MEM -> implementeer write/read-primitiewe
- CP_SET_DRAW_STATE with run-now flags (stuur onmiddellik uit)
GPU R/W-primitiewe via vals pagetable
- Write: CP_MEM_WRITE na ’n deur die aanvaller gekose GPU VA waarvan die PTEs jy aan ’n gekose PA toewys -> arbitrêre fisiese skryf
- Lees: CP_MEM_TO_MEM kopieer 4/8 bytes van teiken-PA na ’n deur userspace gedeelde buffer (gebruik batch vir groter lees)
Notas
- Elke Android-proses kry ’n KGSL context (IOCTL_KGSL_GPU_CONTEXT_CREATE). Om konteksse te wissel werk gewoonlik SMMU-tabelle in die RB by; die fout laat jou toe om dit in SDS te doen.
- Oormatige GPU-verkeer kan UI-onderbrekings en herlaaiings veroorsaak; lees is klein (4/8B) en synchronisasie is standaard traag.
Building the SDS command sequence
- Spray ’n vals GPU pagetable in gedeelde geheue sodat ten minste een voorbeeld op ’n bekende fisiese adres land (bv. via allocator grooming en herhaling).
- Skep ’n SDS-buffer wat, in volgorde, die volgende bevat:
- CP_SMMU_TABLE_UPDATE na die fisiese adres van die vals pagetable
- Een of meer CP_MEM_WRITE en/of CP_MEM_TO_MEM pakkette om R/W te implementeer met jou nuwe vertalings
- CP_SET_DRAW_STATE met vlagte om nou uit te voer
Die presiese pakket-enkoderinge verskil per firmware; gebruik freedreno’s afuc/packet docs om die woorde saam te stel, en verseker dat die SDS-submissiepad deur die bestuurder geneem word.
Finding Samsung kernel physbase under physical KASLR
Samsung randomiseer die kernel physbase binne ’n bekende streek op Snapdragon-toestelle. Brute-force die verwagte reeks en soek die eerste 16 bytes van _stext.
Verteenwoordigende lus
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;
}
}
Sodra physbase bekend is, bereken die kernel virtuele met die lineêre map:
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
Stabilisering na ’n vinnige, betroubare CPU-side kernel R/W (dirty pagetable)
GPU R/W is stadig en van klein granulariteit. Pivot na ’n vinnige/betroubare primitive deur jou eie proses PTEs te korrupteer (“dirty pagetable”):
Stappe
- Lokaliseer die huidige task_struct -> mm_struct -> mm_struct->pgd deur die stadige GPU R/W primitives te gebruik
- mmap twee aangrensende userspace pages A en B (bv., by 0x1000)
- Loop PGD->PMD->PTE om A/B se PTE fisiese adresse op te los (helpers: get_pgd_offset, get_pmd_offset, get_pte_offset)
- Oorskryf B se PTE om na die laaste-level pagetable te wys wat A/B bestuur met RW attributes (phys_to_readwrite_pte)
- Skryf via B se VA om A se PTE te muteer om teiken PFNs te map; lees/skryf kernel memory via A se VA en spoel die TLB totdat ’n sentinel flips
Voorbeeld dirty-pagetable pivot snippet
```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>
## Opsporing
- Telemetrie: waarsku as CP_SMMU_TABLE_UPDATE (of soortgelyke geprivilegieerde opcodes) buite RB/IB0 verskyn, veral in SDS; monitor abnormale uitbarstings van 4/8-byte CP_MEM_TO_MEM en oormatige TLB flush-patrone
## Impak
'n plaaslike app met GPU-toegang kan geprivilegieerde GPU-pakkette uitvoer, die GPU SMMU kaap, enige kernel fisiese/virtuele R/W bereik, SELinux deaktiveer en root verkry op aangetaste Snapdragon A7xx-toestelle (bv. Samsung S23). Ernst: Hoog (kernel kompromittering).
### Sien ook
<a class="content_ref" href="pixel-bigwave-bigo-job-timeout-uaf-kernel-write.md"><span class="content_ref_label">Pixel Bigwave Bigo Job Timeout Uaf Kernel Write</span></a>
## Verwysings
- [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]
> Leer en oefen AWS Hacking:<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;">\
> Leer en oefen GCP Hacking: <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;">
> Leer en oefen Azure Hacking: <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>Ondersteun HackTricks</summary>
>
> - Kyk na die [**subskripsie planne**](https://github.com/sponsors/carlospolop)!
> - **Sluit aan by die** 💬 [**Discord groep**](https://discord.gg/hRep4RUj7f) of die [**telegram groep**](https://t.me/peass) of **volg** ons op **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Deel hacking truuks deur PRs in te dien na die** [**HackTricks**](https://github.com/carlospolop/hacktricks) en [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
>
> </details>


