
ARM Mali GPU 커널 드라이버의 use-after-free를 통한 페이지 캐시 악용을 시연하여 디스크 수정 없이 루트로 임의 코드 실행을 달성하는 CVE-2024-1065용 개념 증명 익스플로잇입니다.
CVE-2024-1065에 대한 개념 증명 익스플로잇으로, ARM Mali GPU 커널 드라이버의 물리적 페이지 use-after-free 취약점입니다. 이 익스플로잇은 페이지 캐시 익스플로잇을 시연합니다. 즉, 해제된 MIGRATE_MOVABLE 페이지를 SUID 바이너리의 메모리 내 코드 이미지로 경쟁적으로 유도한 다음, 이를 덮어써서 디스크의 어떤 파일도 수정하지 않고 루트 권한으로 임의 코드 실행을 달성합니다.
연구 목적으로만 사용하십시오. 이 PoC는 통제된 x86 테스트 시스템에서 패치된 취약점을 대상으로 합니다. 취약한 코드 경로는 프로덕션 Pixel 기기의 사용자 공간에서 트리거할 수 없습니다.
UAF 트리거 — 익명 페이지가 KBASE_IOCTL_MEM_IMPORT를 통해 Mali GPU 드라이버로 가져와지고 사용자 공간에 두 번 매핑됩니다. 두 매핑을 모두 해제하면 물리적 페이지가 MIGRATE_MOVABLE 버디 프리리스트로 반환되지만 cpu_mapping2는 여전히 이를 앨리어싱합니다.
페이지 캐시 스프레이 — 익스플로잇은 /usr/bin/passwd를 100번 열고 posix_fadvise(POSIX_FADV_DONTNEED)로 PAGE_OFFSET의 페이지를 반복적으로 축출한 다음, pread로 캐시 미스를 유발합니다. 각 미스는 커널이 새 MIGRATE_MOVABLE 페이지를 할당하도록 강제하여 UAF 페이지가 선택될 때까지 프리리스트를 순환시킵니다.
중첩 확인 — MAIN_OFFSET에서 cpu_mapping2를 통해 읽은 프로브 바이트는 UAF 페이지가 이제 바이너리의 페이지 캐시 항목을 뒷받침하는지 확인합니다. 0x00 및 0x61 값은 실패를 나타내며, 그 외의 값은 실제 바이너리 코드입니다.
셸코드 주입 — cpu_mapping2를 통한 memcpy는 setuid(0), setgid(0), execve("/bin/sh")를 호출하는 셸코드로 main()을 덮어씁니다.
실행 — execve("/usr/bin/passwd")가 호출됩니다. ELF 로더는 바이너리의 페이지 캐시 페이지를 실행 가능으로 매핑합니다. 손상된 페이지는 이미 존재하므로 디스크 읽기가 발생하지 않으며, 셸코드가 루트 권한으로 실행됩니다.
gcc -O2 -o exploit exploit.c
./exploit
예상 출력:
[*] Opened /usr/bin/passwd x100
[*] MEM_IMPORT: flags=0x... gpu_va=0x... va_pages=0x1
[*] gpu_mapping (VA 1): 0x...
[*] cpu_mapping2 (VA 2): 0x...
[*] UAF triggered — stale mapping alive at 0x...
[*] Spraying page cache (100 attempts)...
[+] Overlap confirmed on attempt N (byte=0xf3) — cpu_mapping2 aliases the page cache!
[*] Shellcode written. Triggering execve...
# id
uid=0(root) gid=0(root) groups=0(root)
대상별 값은 모두 exploit.c 상단에 정의되어 있습니다:
#define TARGET_BINARY "/usr/bin/passwd"
#define PAGE_OFFSET 0x4000 /* file offset of the page containing main() */
#define MAIN_OFFSET 0xbc0 /* intra-page offset of main() */
다른 바이너리로 대상을 변경하려면 엔트리 포인트 주소(readelf -h)에서 _start를 디스어셈블하여 함수 오프셋을 찾은 다음 계산합니다:
PAGE_OFFSET = sym_va & ~0xfff
MAIN_OFFSET = sym_va & 0xfff
실행 전에 검증하세요:
dd if=<TARGET_BINARY> bs=1 skip=$((PAGE_OFFSET + MAIN_OFFSET)) count=8 2>/dev/null | xxd
| 요구 사항 | 값 |
|---|
| 아키텍처 | x86-64 |
| 커널 | 5.15 (5.15.0+에서 테스트됨) |
| 커널 설정 | CONFIG_MALI_NO_MALI=y, CONFIG_MALI_CSF_SUPPORT=y, r48 |
| 장치 | 사용자 공간에서 접근 가능한 /dev/mali0 |
| 대상 바이너리 | /usr/bin/passwd (SUID 루트) |
| 증상 | 원인 | 해결 방법 |
|---|
| 100회 시도 후에도 스프레이 실패 | 레이스 윈도우 전에 UAF 페이지가 소비됨 | UAF 트리거 전에 대상 페이지를 미리 축출하고 NUM_FDS를 늘리십시오 |
kernel BUG at mm/page_poison.c | 0xaa 페이지 포이즌 바이트에서 프로브 검사가 통과함 | 확인 검사에 probe != 0xaa를 추가하십시오 |
MEM_IMPORT에서 SYSCHK 실패 | Mali 드라이버가 로드되지 않았거나 버전이 잘못됨 | CONFIG_MALI_NO_MALI 및 드라이버 버전 핸드셰이크를 확인하십시오 |