
로컬 권한 상승 PoC for CVE-2023-20938, Android binder의 use-after-free 취약점으로, 취약한 테스트 빌드에서 root 권한을 획득하고 SELinux를 비활성화합니다.
CVE-2023-20938에 대한 로컬 권한 상승 개념 증명입니다. Android binder 드라이버의 use-after-free 취약점입니다. 권한이 없는 셸에서 uid 0을 획득하고 SELinux를 permissive로 떨어뜨린 후 루트 셸을 실행합니다.
이 빌드의 대상: Android Cuttlefish / Goldfish AVD, x86_64, 커널 5.10.107.
⚠️ 승인된 연구/교육 목적으로만 사용하세요. 자신의 테스트 장치 및 에뮬레이터에서만 실행하세요. 이 버그는 현재 Android에서 수정되었습니다. 이 PoC는 의도적으로 취약하게 만든 오래된 테스트 빌드를 대상으로 합니다. 보증하지 않습니다.

asciinema로 녹화하고 agg로 GIF로 변환했습니다:
drivers/android/binder.c, binder_transaction_buffer_release()binder_transaction_buffer_release()는 공격자가 제공한 offsets_size를 신뢰합니다. offsets_size가 전체 항목보다 1바이트 부족한 (sizeof(offset) - 1 == 7) 트랜잭션을 보내면 릴리스 경로가 후행 flat_binder_object를 잘못 구문 분석하여 binder_node의 참조 카운트를 초과 감소시키고, 여전히 활성화된 매달린 참조가 있는 상태에서 해제합니다.
이 익스플로잇은 크로스 캐시 누수를 임의 읽기 및 제어된 쓰기로 연결한 다음 자격 증명을 다시 작성합니다. 단계별로 설명합니다 (poc_cf.c의 main() 참조):
B. Cross-cache leak
free a wave of binder_nodes (kmalloc-128) -> page returns to the buddy
allocator -> reclaim the page with eventpoll epitems (eventpoll_epi cache,
0x80-byte objects). A dangling binder transaction now reads kernel
pointers out of an epitem, leaking a struct file and its epitem address.
C. Arbitrary read
re-trigger the UAF and use a sprayed fake binder_node to turn binder's
hlist_del unlink into a controlled *(where) = what write. Two writes
redirect a victim file's f_inode at a fake inode overlaid on the epitem
and mark it S_PRIVATE. FIGETBSZ on the file then returns
*(epitem.event.data + 24): a steerable 4-byte read.
D/E/F. Privilege escalation
defeat KASLR (leak eventfd_fops via f_op), walk init_task's task list to
the launcher's cred, zero its uid/gid set and seccomp.mode, then zero
selinux_state to drop SELinux to permissive.
G. A pre-forked, eventfd-free launcher execs a root shell.
두 개의 권한 없는 프로세스가 UAF를 구동하기 위해 binder 노드를 공유해야 합니다; hwservicemanager의 ITokenManager(createToken /get-by-token)를 통해.
clang -static -Wall -Wextra -o poc_cf poc_cf.c -lpthread
binder.h는 poc_cf.c와 같은 디렉토리에 있어야 합니다. 정적 빌드로 의도적으로 만들어져 단일 바이너리가 툴체인 없이 대상에서 실행됩니다.
Cuttlefish 호스트 패키지 디렉토리에서 일치하는 커널 + initramfs로 AVD를 실행합니다:
HOME=$PWD ./bin/launch_cvd \
--kernel_path=bzImage \
--initramfs_path=initramfs.img
adb push poc_cf /data/local/tmp/
adb shell /data/local/tmp/poc_cf
성공적인 실행은 단계를 거쳐 루트 셸로 종료됩니다:
이 익스플로잇은 확률적입니다. 각 단계는 내부적으로 재시도합니다.
따라서 깔끔한 종료나 게스트 패닉이 발생하더라도 다시 실행하세요 (AVD의 /data는 재부팅 시 지워지므로 패닉 후 바이너리를 다시 푸시하세요).
느리거나 바쁜 대상에서 회수 적중률이 낮으면 대기 시간을 늘리거나 스프레이 횟수를 높이세요 (poc_cf.c 상단 근처의 #define):
구조체 오프셋과 무작위화되지 않은 심볼 기준(*_NOKASLR)은 이 정확한 5.10.107 빌드에 특화되어 있으며 다른 곳에서는 작동하지 않습니다. 다른 커널로 포팅하려면 일치하는 vmlinux의 값으로 poc_cf.c 상단 근처의 오프셋 블록과 심볼 블록을 업데이트하세요. 저는 오프셋을 추출한 후 Pixel 7에서 시도했습니다.
| 매크로 | 기본값 | 의미 |
|---|
SETTLE_US | 5000 | 노드 해제 후, 스프레이 전 일시 중지 |
FAKE_SPRAY_COUNT | 8 | 슬롯 회수를 경쟁하는 차단된 sendmsg() 수 |
FAKE_WAIT_US | 10000 | 스프레이 해제 후, 언링크 전 일시 중지 |
PRESKIP_US | 1000 | 희생 첫 번째 sendmsg 후 일시 중지 |
KW_RETRY_COUNT | 8 | 단일 언링크 쓰기당 시도 횟수 |
STAGE_C_TRIES | 8 | 임의 읽기를 온라인으로 가져오기 위한 시도 |