
CVE-2022-32932 é outra vulnerabilidade que descobri na interface de kernel do ANE; este é um problema de double fetch que resultou em uma interessante escrita OOB.
H11ANEIn::patchMutableSurface() (alcançável a partir de H11ANEIn::ANE_ProgramSendRequest_gated) é chamada se o model.hwx possui um procedimento mutável e também uma seção initInfo. Procurei por um modelo assim, mas não encontrei nenhum, então acabei corrigindo um dos modelos pré-compilados e usei CVE-2022-32845 para carregá-lo. Tenha em mente que CVE-2022-32845 não é necessária para alcançar o caminho de código vulnerável a partir do sandbox padrão de aplicativos; basta compilar um mlmodel personalizado para obter os mesmos resultados. Você pode encontrar mais detalhes sobre CVE-2022-32845 nos meus slides da apresentação
ZinComputeProgramUpdateMutables() é outra função chamada por H11ANEIn::patchMutableSurface() e o protótipo da função é o seguinte:
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: é a seção initInfo que contém uma entrada serializada. Você pode encontrar a função serializadora serialize_initinfo_section() no código-fonte do exploit weightBufs. mutable_procedure_info : é um buffer IOSurface compartilhado fornecido pelo atacante; também é chamado de weightsBuffer no exploit weightBufs. mut_procedure_info_size: indica o tamanho do buffer de superfície mutable_procedure_info. MUTK_kernel_section: (ou MUTK) É um buffer de mapeamento de um objeto IOSurface criado pelo kernel durante a fase de carregamento do programa. MUTK_kernel_section_size: é o tamanho da seção de kernel mutável.

O loop 88-92 calcula a contagem de objetos MutableWeight dentro do objeto mutable_procedure_info e, em seguida, calcula o tamanho da alocação do array MutableWeight na linha 93. Depois disso, o array de objetos ANECMutableWeight é alocado na linha 100 e preenchido com o par buffer/tamanho de peso apropriado no loop 111-127 por ANECGetMutableWeight().
ANECGetMutableOperationInfo() retorna um objeto opsInfo da nossa memória compartilhada:
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;
}
O pseudo-código de ANECGetMutableWeight é o seguinte:
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;
}
O pseudo-código de ANECGetMutableWeightInfo é o seguinte:
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]);
}
Já descrevi o formato do ANECMutableProcedureInfo nos slides “Atacando o Neural Engine da Apple”, então sinta-se à vontade para lê-los se ainda não o leu. A definição da estrutura pode ser encontrada no exploit weightBufs em ‘ aneProgram.h’.
Se você notou, ANECGetMutableOperationInfo()->op_count é buscado duas vezes: uma para calcular o tamanho a fim de alocar o array ANECMutableWeight, e outra para preencher esse array.
Como o buffer mutable_procedure_info é uma memória compartilhada, um atacante poderia usar uma thread separada para alterar o valor de opsInfo->op_count entre o primeiro e o segundo usos, resultando em uma incompatibilidade de tamanho que levará a uma interessante escrita OOB em uma kalloc var zone, kheap defaul ou no kernel map.
A vulnerabilidade pode ser usada de muitas maneiras interessantes. Por exemplo, um atacante poderia definir total_count = 0x1000; na linha 93 e, em seguida, aumentar opsInfo->count para algo maior, fazendo com que os dados sejam copiados para fora dos limites em ANECGetMutableWeight().
O kernel entrará em pânico na instrução mostrada abaixo se a escrita OOB atingir uma área de memória não mapeada:
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
Esse bug fornece uma primitiva forte, pois escreve dois valores de 64 bits: um endereço de kernel apontando para o nosso buffer compartilhado de usuário e um valor de 64 bits (semi-)arbitrário.
A prova de conceito fica como exercício para o leitor. No entanto, o exploit weightBufs inclui tudo o que é necessário para alcançar o caminho de código vulnerável. Boa sorte :-).