
CVE-2022-32932(AppleのANEカーネルドライバにおけるダブルフェッチの脆弱性で、共有メモリ操作による境界外書き込みに至る)の技術的分析と概念実証。
CVE-2022-32932 は、私が ANE カーネルインターフェースで発見した別の脆弱性です。これはダブルフェッチ問題であり、興味深い OOB 書き込みを引き起こします。
H11ANEIn::patchMutableSurface() (H11ANEIn::ANE_ProgramSendRequest_gated から到達可能) は、model.hwx に可変手続き (mutable procedure) があり、かつ initInfo セクションがある場合に呼び出されます。私はそのようなモデルを探しましたが見つからなかったため、最終的にプリコンパイル済みモデルの1つをパッチし、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 は mutable_procedure_info オブジェクト内の MutableWeight オブジェクト数を計算し、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 は2回フェッチされます。1回目は ANECMutableWeight 配列を割り当てるサイズを計算するため、2回目はこの配列を埋めるためです。
mutable_procedure_info バッファは共有メモリであるため、攻撃者は別のスレッドを使用して、1回目と2回目の使用の間に opsInfo->op_count の値を変更することができ、その結果サイズの不一致が生じ、kalloc var ゾーン、kheap defaul、またはカーネルマップのいずれかで興味深い OOB 書き込みにつながります。
この脆弱性は多くの興味深い方法で悪用される可能性があります。例えば、攻撃者は行93 で total_count = 0x1000; に設定し、その後 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] // <---- カーネルパニック
com.apple.driver.AppleH11ANEInterface:__text:FFFFFE0008913D14 RET
このバグは、2つの64ビット値(ユーザー共有バッファを指すカーネルアドレスと(半)任意の64ビット値)を書き込む強力なプリミティブを提供します。
概念実証は読者の課題として残されています。ただし、weightBufs エクスプロイトには脆弱なコードパスに到達するために必要なすべてのものが含まれています。幸運を祈ります :-)。