
CVE-2026-46215에 대한 개념 증명 익스플로잇으로, Linux DRM GEM change_handle ioctl에서 발생하는 use-after-free 취약점입니다. 슬랩 재활용(slab reclamation) 및 DirtyPipe 방식의 페이지 캐시 덮어쓰기를 통해 권한 없는 로컬 사용자에서 루트로의 권한 상승을 시연합니다.
change_handle Use-After-Free (비특권 LPE)DRM 코어 ioctl DRM_IOCTL_GEM_CHANGE_HANDLE(drm_gem_change_handle_ioctl, ioctl nr 0xD2)의 사용 후 해제(use-after-free)를 통한 로컬 권한 상승. 렌더 노드(/dev/dri/renderD*에 접근 가능한 모든 사용자에게 도달 가능하며, 모든 주요 데스크톱 배포판에서 systemd-logind가 활성 세션에 부여함). 이 저장소의 체인은 비특권 사용자로부터 암호 없는 root까지 UAF를 이끌어냅니다.
AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)53096728b891 ("drm: Add DRM prime interface to reassign GEM handle", David Francis / AMD), AMD의 CRIU 작업을 위해 추가됨5e28b7b94408). 이 ioctl은 이 버그 및 관련 경쟁 조건 때문에 7.1에서 업스트림에서 비활성화되고 있습니다.이 버그는 먼저 Puttimet Thammasaeng이 보고했으며, 그는 수정 사항에 대해 업스트림 Reported-by 크레딧을 보유합니다. 저는 2026-04-12에 [email protected]에 독립적으로 발견하여 보고했습니다. 제 보고는 인정되어 유지관리자에게 전달되었지만, 이전 보고가 업스트림에서 크레딧을 받았습니다. 이 저장소에는 제 분석과 익스플로잇이 포함되어 있습니다.
Writeup: https://cyberstan.co.uk
수정 사항은 안정적인 커널(6.18.32 및 7.0.9 이상)에 출시되었습니다. 이 저장소는 수정 사항이 널리 사용 가능해진 후 공개되었습니다.
drm_gem_change_handle_ioctl()은 GEM 객체를 한 핸들에서 다른 핸들로 이동하지만 obj->handle_count를 조정하지 않습니다. 또한 drm_vma_node_allow/revoke와 드라이버 open/close 콜백을 건너뜁니다. handle_count가 1로 유지되기 때문에, 이전 핸들에 대한 동시 GEM_CLOSE는 이를 0으로 낮추고 객체를 해제하는 반면, 새 핸들은 여전히 IDR에서 객체를 참조합니다. 이 매달린 핸들은 나중에 drm_gem_object_release_handle()에서 역참조되는 use-after-free입니다.
GEM_CHANGE_HANDLE을 GEM_CLOSE와 경쟁하여 매달린 핸들을 얻습니다.pipe_buffer 배열(kmalloc-512 조건을 맞추기 위한 msg_msg 펑수이, 그 다음 splice로 채워진 파이프)로 회수합니다.pipe_buf_ops를 누출합니다: obj->size(오프셋 216)가 pipe_buf[5].ops와 겹쳐 커널 포인터와 KASLR 베이스를 제공합니다.obj->name(오프셋 224)이 pipe_buf[5].flags에 위치하고 PIPE_BUF_FLAG_CAN_MERGE(name = 16 = 0x10)를 설정하도록 합니다./etc/passwd이며, root는 암호 없게 됩니다.오프셋은 pahole을 통해 확인되었으며 6.18부터 7.0까지의 레이아웃에 특화되어 있습니다. 다른 커널의 경우 GEM_* / PIPEBUF_* 정의를 재정의하십시오.
poc.c - 익스플로잇. -lpthread로 정적 빌드.run_exploit.sh - PoC와 최소 initramfs를 빌드하고 QEMU에서 부팅합니다.qemu-system-x86_64, gcc, busybox (정적), fakeroot, cpio, gzip. KVM(/dev/kvm)이 권장되며, 경쟁 조건이 훨씬 더 신뢰할 수 있습니다.
익스플로잇은 특정 옵션으로 빌드된 취약한 타겟이 필요합니다.
CONFIG_KASAN은 반드시 OFF여야 합니다. KASAN은 해제된 슬랩을 격리하고 파이프 스프레이 회수를 차단하므로, KASAN 빌드에서는 익스플로잇이 작동하지 않습니다.virtio_gpu(데모에서 사용) 또는 nouveau.CONFIG_DRM=y, CONFIG_DRM_VIRTIO_GPU=y, CONFIG_DEVTMPFS=y, CONFIG_BLK_DEV_INITRD=y.단계:
CONFIG_KASAN이 설정되지 않았는지 확인합니다.make -j"$(nproc)" bzImage, arch/x86/boot/bzImage 생성.데모는 nokaslr로 부팅되므로 누출된 포인터가 결정적입니다. 누출 자체가 KASLR을 무력화하므로 KASLR이 켜져 있어도 작동하며, 주소는 부팅마다 다릅니다.
drivers/gpu/drm/drm_gem.c 파일, drm_gem_change_handle_ioctl() 함수를 확인하십시오.
빠른 확인:
awk '/^int drm_gem_change_handle_ioctl/,/^}/' \
drivers/gpu/drm/drm_gem.c | grep -c handle_count
0 = 취약 (참조 카운트 처리 없음).구조별:
file_priv->prime.lock 획득, 단일 idr_alloc(&file_priv->object_idr, obj, ...), 그 다음 이전 핸들에 대해 idr_remove(). drm_gem_object_handle_get 없음.idr_alloc 후 idr_replace(NULL)로 이전 핸들 분리), 프라임 연산이 성공한 후에야 실제 객체가 교체됨../run_exploit.sh /path/to/bzImage
이 명령은 poc.c를 빌드하고 initramfs로 감싼 후 QEMU를 -device virtio-gpu-pci 및 nokaslr로 부팅합니다. PoC는 uid 1000(비특권)으로 실행되며, 스크립트는 이전과 이후의 /etc/passwd를 출력하고 셸로 떨어뜨립니다. poweroff -f 또는 Ctrl-A X로 VM을 종료합니다.
[!] Race won (iter 977): handle=132049
[!] KASLR: pipe_buf_ops = 0xffffffff82428400
[!] EXPLOIT SUCCESSFUL
[!] FLINK: 16 = 0x10
[*] /etc/passwd:
root::0:0:pwned:/root:/bin/sh
[!] LPE CONFIRMED
[!] root account is now passwordless
root 소유의 읽기 전용(chmod 444) /etc/passwd가 비특권 프로세스에 의해 덮어써집니다. root: 행에서 암호 필드가 사라집니다.
패치된 트리(6.18.32, 7.0.9, 7.1-rc3 이상, 또는 위의 패치된 검사를 통과하는 모든 트리)로 다시 빌드하고 다시 실행:
./run_exploit.sh /path/to/patched-bzImage
패치된 커널에서는 익스플로잇이 "EXPLOIT SUCCESSFUL"에 도달하지 않아야 합니다: 경쟁 조건이 더 이상 새 핸들 아래에서 객체를 해제하지 않으므로 누출이 유효한 커널 포인터를 반환하지 않습니다.
업스트림 수정 사항이 출시된 후 연구 및 방어 목적으로 공개되었습니다. 있는 그대로 제공됩니다. 본인이 제어하는 VM에서만 실행하십시오.