
CVE-2016-6187에 대한 커널 익스플로잇 (로컬 권한 상승)
CVE-2016-6187용 커널 익스플로잇 (로컬 권한 상승) (영향 버전: < 4.6.5)
익스플로잇은 커스텀 컴파일된 4.6 커널 + buildroot를 대상으로 개발되었습니다 (kconfig는 곧 제공 예정)
그리고 KASLR, SMEP, SMAP을 무력화합니다 (KPTI는 4.6에서 사용 불가)
악용된 취약점은 AppArmor의 LSM 후크(setprocattr) 중 하나에서 발생하는 힙 기반 단일 널바이트 오버플로입니다. 자세한 내용은 https://nvd.nist.gov/vuln/detail/CVE-2016-6187 참조
초기 측정 결과 루트 셸을 획득할 확률이 94.44%임을 보여줍니다. 18번 중 1번의 경우 익스플로잇이 커널을 oops시킵니다. 루트 액세스 후 커널을 완전히 안정화하려면 추가 작업이 필요합니다.
널바이트 오버플로를 악용하여 kmalloc-128의 freelist 포인터의 LSB를 손상시켜 이미 할당된 객체를 가리키도록 합니다. 그 후 해당 할당된 객체를 해제하여 이중 해제 시나리오를 발생시킵니다.
이제 kmalloc-128 캐시에서 두 개의 중첩된 구조체를 할당할 수 있습니다. 첫 번째 구조체는 사용자 공간에서 다시 읽을 수 있는 내용을 가진 객체입니다. 이를 위해 ip6_sf_socklist를 선택했습니다. 두 번째 구조체는 ip6_sf_socklist 위에 할당되며 커널 포인터를 포함해야 합니다 - rfkill_data가 적합합니다. 여기에는 힙 포인터(빈 rfkill_data.events 목록)와 rfkill_fds를 가리키는 전역 데이터 포인터(rfkill_data.list.prev)가 포함됩니다. rfkill_data가 ip6_sf_socklist 위에 할당되면 getsockopt(sock, IPPROTO_IPV6, MCAST_MSFILTER, gsf, &optlen)을 호출하여 커널 포인터를 검색할 수 있습니다. 그런 다음 rfkill_fds에서 커널 텍스트 베이스를 유도할 수 있습니다.
그 다음 두 번째 이중 해제 시나리오를 발생시키지만, 이번에는 메시지 큐의 연결 프로세스를 악용하여 freelist 포인터를 덮어쓰고 할당자가 kmalloc-128에서 청크를 요청할 때 kmalloc-96에서 청크를 반환하도록 속입니다.
그런 다음 크기 불일치를 활용하여 kmalloc-96 청크의 freelist 포인터로 오버플로하여 임의 주소를 할당할 수 있는 프리미티브를 얻습니다. 우리의 경우 ptmx_fops 구조체를 대상으로 하고 ptmx_fops.unlocked_ioctl 콜백을 스택 피벗 가젯에 대한 포인터로 덮어씁니다.
/dev/ptmx에서 ioctl을 호출하면 피벗 가젯이 트리거되고 스택을 kmalloc-128의 가짜 스택으로 피벗합니다. 이 가짜 스택에는 이미 첫 번째 단계 ROP 체인이 준비되어 있습니다.
kmalloc-128 가짜 스택의 공간이 제한적이므로(다음 청크가 동일한 slab에 있을지 알 수 없음), 첫 번째 단계 ROP 체인의 작업은 두 번째 단계 ROP 체인을 사용자 공간에서 커널 dmesg 로그 버퍼(__log_buf)로 복사하고 스택을 __log_buf로 피벗하는 것입니다.
그런 다음 두 번째 단계 ROP 체인은 권한을 상승시키기 위해 commit_creds(prepare_kernel_cred(NULL))을 호출하기 전에 커널을 복구(ptmx_fops 복원 등)하려고 시도합니다.
마지막으로 iretq를 통해 커널 공간을 종료하고 상승된 권한으로 execve("/bin/sh", 0, 0) 서브루틴으로 돌아갑니다.
권한이 없는 사용자는 최소한 /dev/rfkill을 열 수 있어야 합니다. 일부 배포판에서는 이를 허용하지 않을 수 있습니다. Ubuntu와 Fedora를 확인했으며, 둘 다 권한이 없는 사용자가 /dev/rfkill을 열 수 있도록 허용합니다(Fedora는 AppArmor 대신 SELinux를 사용하지만).
다음 섹션에서는 qemu-x86_64에서 로컬 권한 상승을 재현하는 방법을 설명합니다.
먼저 initrd와 bzImage 커널을 얻어야 합니다. 두 가지 방법이 있습니다:
가장 쉽고 빠른 방법입니다. 필요한 모든 구성 요소는 prebuilt/에서 찾을 수 있습니다.
또는 직접 구성 요소를 빌드할 수도 있습니다. 4.6 커널을 가져와서 config/에 제공된 x86_64_vuln_kern_defconfig를 사용하여 bzImage를 컴파일하세요.
initrd의 경우 예를 들어 buildroot를 사용할 수 있습니다. init에서 /dev/rfkill에 올바른 권한을 설정해야 합니다.
chmod 664 /dev/rfkill
이는 Ubuntu-16.04 및 다른 많은 배포판과 동일합니다. 또한 익스플로잇을 컴파일하여 initrd에 넣는 것을 잊지 마십시오.
make
구성 요소를 얻었으면 취약한 머신을 부팅합니다.
./boot.sh <path/to/bzImage> <path/to/rootfs.cpio>
그리고 익스플로잇을 실행합니다
./exploit
이미 0인 LSB를 널로 만들었는지 여부에 따라 익스플로잇을 여러 번 실행해야 할 수도 있습니다. 또한 루트 액세스 권한을 얻은 후에도 약간의 충돌 가능성(아마 10%~20%?)이 있음을 참고하십시오 - 작업 중입니다.
즐기세요.
ptmx_fops 복구kmalloc-128 freelist 복구