
CVE-2022-32932 è un'altra vulnerabilità che ho scoperto nell'interfaccia kernel ANE; si tratta di un problema di double fetch che ha portato a un'interessante scrittura OOB.
H11ANEIn::patchMutableSurface() (raggiungibile da H11ANEIn::ANE_ProgramSendRequest_gated) viene chiamata se il model.hwx ha una procedura mutabile e ha anche una sezione initInfo; ho cercato un modello del genere ma non ne ho trovato nessuno, quindi ho finito per patchare uno dei modelli precompilati e ho usato CVE-2022-32845 per caricarlo. Tieni presente che CVE-2022-32845 non è necessario per raggiungere il percorso di codice vulnerabile dalla sandbox predefinita dell'app; è sufficiente compilare un mlmodel personalizzato per ottenere gli stessi risultati. Puoi trovare maggiori dettagli su CVE-2022-32845 nelle mie slide della presentazione.
ZinComputeProgramUpdateMutables() è un'altra funzione chiamata da H11ANEIn::patchMutableSurface() e il prototipo della funzione è il seguente:
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: è la sezione initInfo che contiene un input serializzato; puoi trovare la funzione di serializzazione serialize_initinfo_section() nel codice sorgente dell'exploit weightBufs. mutable_procedure_info: è un buffer IOSurface condiviso fornito dall'attaccante; è anche chiamato weightsBuffer nell'exploit weightBufs. mut_procedure_info_size: indica la dimensione del buffer di superficie mutable_procedure_info. MUTK_kernel_section: (o MUTK) è un buffer di mappatura di un oggetto IOSurface creato dal kernel durante la fase di caricamento del programma. MUTK_kernel_section_size: è la dimensione della sezione kernel mutabile.

Il ciclo 88-92 calcola il numero di oggetti MutableWeight all'interno dell'oggetto mutable_procedure_info, quindi calcola la dimensione di allocazione dell'array MutableWeight a 93. Dopodiché, l'array di oggetti ANECMutableWeight viene allocato a 100, e poi popolato con la coppia appropriata buffer di peso/dimensione nel ciclo 111-127 da ANECGetMutableWeight().
ANECGetMutableOperationInfo() restituisce un oggetto opsInfo dalla nostra memoria condivisa:
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;
}
Lo pseudo-codice di ANECGetMutableWeight è il seguente:
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;
}
Lo pseudo-codice di ANECGetMutableWeightInfo è il seguente:
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]);
}
Ho già descritto il formato di ANECMutableProcedureInfo nelle slide “Attacking Apple’s Neural Engine”, quindi sentiti libero di leggerle se non l'hai già fatto. La definizione della struttura si trova nell'exploit weightBufs in ' aneProgram.h’.
Se hai notato, ANECGetMutableOperationInfo()->op_count viene recuperato due volte: una volta per calcolare la dimensione necessaria ad allocare l'array ANECMutableWeight, e un'altra per popolare questo array.
Poiché il buffer mutable_procedure_info è una memoria condivisa, un attaccante potrebbe usare un thread separato per cambiare il valore di opsInfo->op_count tra il primo e il secondo utilizzo, causando una discrepanza di dimensioni che porterà a un'interessante scrittura OOB in una zona kalloc var, in kheap defaul o nella mappa del kernel.
La vulnerabilità può essere sfruttata in molti modi interessanti. Ad esempio, un attaccante potrebbe impostare total_count = 0x1000; alla riga 93, quindi aumentare opsInfo->count a un valore più grande, facendo sì che i dati vengano copiati fuori dai limiti in ANECGetMutableWeight().
Il kernel andrà in panic all'istruzione mostrata sotto se la scrittura OOB ha raggiunto un'area di memoria non mappata:
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
Questo bug fornisce una primitiva potente in quanto scrive due valori a 64 bit: un indirizzo kernel che punta al nostro buffer condiviso utente e un valore a 64 bit (semi-)arbitrario.
La prova di concetto è lasciata come esercizio per il lettore. Tuttavia, l'exploit weightBufs include tutto il necessario per raggiungere il percorso di codice vulnerabile. Buona fortuna :-).