
desc_race эксплойт для iOS 15.0 - 15.1.1 (со стабильными примитивами чтения/записи ядра) (CVE-2021-30955)
Эксплойт "desc_race" (CVE-2021-30955) для iOS 15.0 - 15.1.1 (со стабильными примитивами чтения/записи в ядре)
Увеличьте ёмкость массива IOSurfaceClient до 0x2000, цель — записать указатель, содержимое которого полностью контролируется, а затем использовать интерфейсы IOSurfaceRootUserClient для достижения чтения/записи в ядре. Размер массива составляет 0x2000 * 8 байт, поэтому он размещается в большой области KHEAP_KEXT, которая совпадает с KHEAP_DEFAULT.
Затем выделите буфер ядра размером 0x4000 байт, используя вспомогательное сообщение kmsg с дескриптором ool размером 0x4000 байт, который будет перезаписан с конца. После этого вызовите ошибку с выделенным сообщением, называемым двойным copyin kmsg, размещённым сразу за вспомогательным буфером ядра. Это сообщение содержит дескриптор ool ports, количество элементов которого равно 0x2000, поэтому оно будет довольно близко к массиву IOSurfaceClient.
Затем вы должны получить вспомогательное сообщение. Если гонка удалась, дескриптор ool ports будет раскрыт, и мы сможем определить адрес массива IOSurfaceClient.
Затем снова выделите буфер ядра размером 0x4000 байт — он займёт упомянутый выше буфер ядра. На этот раз я конструирую поддельный заголовок kmsg с корректным телом. Затем я уничтожаю двойной copyin kmsg, и ядро начнёт работу с нашего поддельного заголовка. Я использую поддельный mach_msg_ool_descriotpor_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 находится под нашим контролем, что даёт нам идеальный примитив чтения/записи. Я использую это для записи контролируемого указателя в массив IOSurfaceClient, а затем достигаю чтения/записи в ядре в сочетании с интерфейсами IOSurfaceRootUserClient.
Я перечитывал пост bazad "One byte to rule them all" снова и снова во время разработки этого эксплойта и использовал ту же технику — подделку vm_copy_t — в его эксплойте. Но есть некоторые отличия.
XNU подписывает сообщение, и мы больше не можем получить повреждённый kmsg.
В процедуре уничтожения я устанавливаю mapping_in_progress у vm_object, чтобы заставить ядро вращаться, и таким образом избегаю паники из-за проверки зоны.
Огромное спасибо bazad за его отличный пост, WangTielei за то, что сообщил мне, что интерфейсы IOSurfaceClient теперь непригодны для примитива чтения/записи, и pedantcoder за его доброту.