
PinTheft(CVE-2026-43494)의 Go 구현
pintheft의 Go 포팅 - RDS zcopy double-free LPE. Dirty Frag 취약점 클래스에 속합니다.
[!IMPORTANT] Arch Linux는 RDS 모듈이 기본적으로 로드될 수 있는 몇 안 되는 배포판 중 하나이므로, 이 취약점에 가장 많이 노출된 배포판 중 하나입니다.
버그는 rds_message_zcopy_from_user()에 있습니다. 이 함수는 FOLL_GET을 통해 사용자 페이지를 한 번에 하나씩 GUP-핀합니다. 이후 페이지에서 폴트가 발생하면(예: PROT_NONE 가드 페이지), 오류 경로는 이미 핀된 페이지에 대해 put_page()를 호출한 다음, op_mmp_znotifier가 NULL로 설정되었지만 op_nents/sg 항목은 그대로 남아 있기 때문에 rds_message_purge()가 해당 페이지에 대해 __free_page()를 다시 호출합니다. 페이지에 다른 참조가 여전히 남아 있으면 __free_page는 조용히 refcount를 감소시킵니다. 실패하는 각 sendmsg는 첫 번째 페이지에서 정확히 하나의 ref를 빼앗습니다.
CONFIG_INIT_ON_ALLOC_DEFAULT_ON 우회io_uring REGISTER_BUFFERS를 통해 대상 페이지를 핀합니다. 이는 FOLL_PIN을 통해 refcount에 GUP_PIN_COUNTING_BIAS(1024)를 추가합니다. 실패하는 zcopy 전송으로 1024개의 핀 ref를 모두 빼앗습니다. 이제 페이지 refcount는 ~1입니다(PTE 매핑뿐). munmap은 해제 전에 mem_cgroup_uncharge(memcg_data 클리어)를 호출하는 일반적인 __folio_put 경로를 사용합니다. bad_page 검사는 실행되지 않습니다.
io_uring은 활성 검사 없이 bvec 배열에 원시 struct page*를 유지합니다. 페이지가 suid 바이너리의 페이지 캐시로 재확보된 후, IORING_OP_READ_FIXED는 해당 댕글링 포인터를 통해 우리의 페이로드를 그 안에 씁니다.
IORING_REGISTER_CLONE_BUFFERS는 imu->refs를 증가시킵니다. 포크된 데몬 자식 프로세스가 클론 링 fd를 열어 둔 상태로 유지합니다 - io_buffer_unmap은 refs > 1을 확인하고 unpin_user_folio를 건너뛰어 해제된 페이지에 대한 refcount 손상을 방지합니다.
CPU 0에 핀한 다음, 해제 전에 오래된 PCP 항목을 비웁니다 - 페이지 캐시 할당자가 페이지를 가져갈 때 우리 페이지는 맨 위에 위치합니다.
REGISTER_BUFFERS(+1024) -> CLONE_BUFFERS(imu->refs=2) ->
daemon holds clone -> steal 1024 refs -> evict page cache ->
drain PCP -> munmap(free) -> pread(reclaim) ->
READ_FIXED(overwrite) -> verify -> exec -> root
go build -o pintheft-go .
./pintheft-go
성공 시 PTY를 통해 루트 셸로 진입합니다. 디스크의 바이너리는 변경되지 않습니다.
sudo cp /tmp/.backup_pintheft_<pid> /usr/bin/su && sudo chmod u+s /usr/bin/su
CONFIG_RDS=m, CONFIG_RDS_TCP=m (SO_RDS_TRANSPORT=2를 통해 자동 로드됨)io_uring_disabled=0인 CONFIG_IO_URING=y그나저나 저는 Arch Linux를 사랑합니다 :)