
Технический анализ и доказательство концепции для CVE-2022-32932, уязвимости двойной выборки в драйвере ядра ANE от Apple, приводящей к записи за пределами буфера через манипуляции с общей памятью.
CVE-2022-32932 — ещё одна уязвимость, которую я обнаружил в интерфейсе ядра ANE; это проблема двойной выборки, приведшая к интересной записи за границы буфера (OOB).
Функция H11ANEIn::patchMutableSurface() (достижимая из H11ANEIn::ANE_ProgramSendRequest_gated) вызывается, если в model.hwx есть изменяемая (mutable) процедура, а также секция initInfo. Я искал такую модель, но не нашёл, поэтому в итоге пропатчил одну из предварительно скомпилированных моделей и использовал CVE-2022-32845 для её загрузки. Имейте в виду, что CVE-2022-32845 не требуется для достижения уязвимого пути кода из стандартной песочницы приложения; достаточно скомпилировать собственный mlmodel, чтобы получить тот же результат. Подробнее о CVE-2022-32845 можно узнать в моих слайдах презентации
ZinComputeProgramUpdateMutables() — ещё одна функция, вызываемая из H11ANEIn::patchMutableSurface(). Её прототип выглядит так:
ZinComputeProgramStatus __cdecl ZinComputeProgramUpdateMutables(
uint64_t procedureId,
const ZinComputeProgramInitInfo *init_info,
const ANECMutableProcedureInfo *mutable_procedure_info,
uint64_t mut_procedure_info_size,
void *MUTK_kernel_section,
uint64_t MUTK_kernel_section_size);
init_info: секция initInfo, содержащая сериализованный вход. Функцию сериализации serialize_initinfo_section() можно найти в исходном коде эксплоита weightBufs. mutable_procedure_info: общий буфер IOSurface, предоставленный атакующим; в эксплоите weightBufs он также называется weightsBuffer. mut_procedure_info_size: обозначает размер буфера поверхности mutable_procedure_info. MUTK_kernel_section: (или MUTK) Это буфер отображения объекта IOSurface, создаваемый ядром на этапе загрузки программы. MUTK_kernel_section_size: размер изменяемой секции ядра.

Цикл 88-92 подсчитывает количество объектов MutableWeight внутри объекта mutable_procedure_info, затем в строке 93 вычисляется размер выделения для массива MutableWeight. После этого в строке 100 выделяется массив объектов ANECMutableWeight, а затем в цикле 111-127 он заполняется соответствующими парами «буфер весов / размер» с помощью ANECGetMutableWeight().
Функция ANECGetMutableOperationInfo() возвращает объект opsInfo из нашей общей памяти:
opsInfo *__fastcall ANECGetMutableOperationInfo(const ANECMutableProcedureInfo *MutableProcedureInfo, unsigned int id)
{
unsigned int weight_buffer_size; // w8
opsInfo *opInfo; // x0
weight_buffer_size = MutableProcedureInfo->header.weight_buffer_size;
if ( !weight_buffer_size )
return 0LL;
opInfo = (opsInfo *)((char *)MutableProcedureInfo + MutableProcedureInfo->wb_offsets[id]);
while ( opInfo->op_index != id )
{
if ( !--weight_buffer_size )
return 0LL;
}
return opInfo;
}
Псевдокод ANECGetMutableWeight:
void __fastcall ANECGetMutableWeight(
const ANECMutableProcedureInfo *procedure_info,
weightInfo *a2,
ANECMutableWeight *a3)
{
uint64_t wi_size; // x9
wi_size = a2->wi_size;
a3->_weightBuf = (char *)procedure_info + a2->wi_off;
a3->_weightBufSize = wi_size;
}
Псевдокод ANECGetMutableWeightInfo:
weightInfo *__fastcall ANECGetMutableWeightInfo(
const ANECMutableProcedureInfo *MutableProcedureInfo,
opsInfo *a2,
unsigned int a3)
{
if ( a2->op_count <= a3 )
return 0LL;
else
return (weightInfo *)((char *)MutableProcedureInfo + a2->op_offsets[a3]);
}
Я уже описал формат ANECMutableProcedureInfo в слайдах «Attacking Apple’s Neural Engine», так что, если ещё не читали, можете ознакомиться. Определение структуры можно найти в эксплоите weightBufs в файле «aneProgram.h».
Как вы могли заметить, ANECGetMutableOperationInfo()->op_count выбирается дважды: один раз для вычисления размера с целью выделения массива ANECMutableWeight, и второй раз — для заполнения этого массива.
Поскольку буфер mutable_procedure_info является разделяемой памятью, атакующий может использовать отдельный поток для изменения значения opsInfo->op_count между первым и вторым использованием, что приводит к несоответствию размеров и, как следствие, к интересной записи за границы (OOB) в зоне kalloc var, kheap default или в карте ядра.
Уязвимость можно использовать множеством интересных способов. Например, атакующий может установить total_count = 0x1000; в строке 93, а затем увеличить opsInfo->count до большего значения, что вызовет копирование данных за границы в ANECGetMutableWeight().
Ядро запаникует на инструкции, показанной ниже, если запись OOB достигнет неотображённой области памяти:
com.apple.driver.AppleH11ANEInterface:__text:FFFFFE0008913D08 EXPORT _ANECGetMutableWeight
com.apple.driver.AppleH11ANEInterface:__text:FFFFFE0008913D08 _ANECGetMutableWeight ; CODE XREF: _ZinComputeProgramUpdateMutables+270↓p
com.apple.driver.AppleH11ANEInterface:__text:FFFFFE0008913D08 LDP X8, X9, [X1,#8]
com.apple.driver.AppleH11ANEInterface:__text:FFFFFE0008913D0C ADD X8, X0, X8
com.apple.driver.AppleH11ANEInterface:__text:FFFFFE0008913D10 STP X8, X9, [X2] // <---- Kernel panic
com.apple.driver.AppleH11ANEInterface:__text:FFFFFE0008913D14 RET
Эта ошибка предоставляет мощный примитив: она записывает два 64-битных значения: адрес ядра, указывающий на наш общий пользовательский буфер, и (полу-)произвольное 64-битное значение.
Доказательство концепции оставляется читателю в качестве упражнения. Однако эксплоит weightBufs включает всё необходимое для достижения уязвимого пути кода. Удачи :-).