
# iOS 15.0 - 15.1.1용 desc_race 익스플로잇 (안정적인 커널 r/w 프리미티브 포함) (CVE-2021-30955)
"desc_race"(CVE-2021-30955) iOS 15.0 - 15.1.1용 익스플로잇(안정적인 커널 r/w 프리미티브 포함)
IOSurfaceClient 배열의 용량을 0x2000으로 늘립니다. 목표는 내용이 완전히 제어되는 포인터를 작성한 다음 IOSurfaceRootUserClient 인터페이스를 사용하여 커널 r/w를 달성하는 것입니다. 배열의 크기는 0x2000 * 8바이트이므로 KHEAP_DEFAULT와 동일한 KHEAP_KEXT의 large map에 위치합니다.
그런 다음 0x4000바이트 ool 디스크립터가 포함된 보조 kmsg를 사용하여 0x4000바이트 커널 버퍼를 할당합니다. 이 버퍼는 뒤에서부터 덮어쓰여집니다. 그런 다음 할당된 메시지로 버그를 트리거합니다. 이 메시지는 double copyin kmsg라고 하며 보조 커널 버퍼 바로 뒤에 배치됩니다. 이 메시지에는 count가 0x2000인 ool ports 디스크립터가 포함되어 있어 IOSurfaceClient 배열에 상당히 가깝게 위치하게 됩니다.
그런 다음 보조 메시지를 수신해야 합니다. 레이싱이 성공하면 ool ports 디스크립터가 노출되고 IOSurfaceClient 배열 주소를 찾을 수 있습니다.
그런 다음 0x4000바이트 커널 버퍼를 다시 할당합니다. 이번에는 앞서 언급한 커널 버퍼를 차지하게 됩니다. 이번에는 적절한 본문을 가진 가짜 kmsg 헤더를 구성합니다. 그런 다음 double copyin kmsg를 파괴하면 커널이 가짜 헤더로 시작합니다. 완전히 제어된 복사본으로 가짜 mach_msg_ool_descriptor_t를 사용하여 **vm_copy_discard()**를 트리거합니다. 복사본을 파괴하는 동안 가장 중요한 줄은 _vm_map_entry_unlink_ll에 있습니다:
1
2
3
4
5
6
#define _vm_map_entry_unlink_ll(hdr, entry) \
MACRO_BEGIN \
(hdr)->nentries--; \
(entry)->vme_next->vme_prev = (entry)->vme_prev; \
(entry)->vme_prev->vme_next = (entry)->vme_next; \
MACRO_END
entry는 우리가 제어하므로 완벽한 r/w 프리미티브를 제공합니다. 이를 사용하여 IOSurfaceClient 배열에 제어된 포인터를 작성한 다음 IOSurfaceRootUserClient 인터페이스와 결합하여 커널 r/w를 달성합니다.
이 익스플로잇을 개발하는 동안 bazad의 게시물 "One byte to rule them all"을 여러 번 읽었으며, 그의 익스플로잇에서 동일한 기법인 vm_copy_t 위조를 사용했습니다. 하지만 몇 가지 차이점이 있습니다.
XNU는 메시지에 서명하므로 더 이상 손상된 kmsg를 수신할 수 없습니다.
파괴 절차에서 vm_object의 mapping_in_progress를 설정하여 커널이 회전하도록 하여 zone 검사로 인한 패닉을 방지합니다.
훌륭한 게시물을 작성해 준 bazad, IOSurfaceClient 인터페이스가 더 이상 r/w 프리미티브에 유효하지 않다는 것을 알려준 WangTielei, 그리고 친절하게 도움을 준 pedantcoder에게 큰 감사를 드립니다.