
CVE-2022-32932 هي ثغرة أخرى اكتشفتها في واجهة نواة ANE؛ وهي مشكلة جلب مزدوج (double fetch) أدت إلى كتابة خارج النطاق (OOB write) مثيرة للاهتمام.
يتم استدعاء H11ANEIn::patchMutableSurface() (والتي يمكن الوصول إليها من H11ANEIn::ANE_ProgramSendRequest_gated) إذا كان model.hwx يحتوي على إجراء قابل للتعديل (mutable procedure) ويحتوي أيضاً على قسم initInfo. بحثت عن مثل هذا النموذج ولكن لم أتمكن من العثور على أي منه، لذلك انتهى بي الأمر بتعديل أحد النماذج المترجمة مسبقاً واستخدمت CVE-2022-32845 لتحميله. يرجى الأخذ في الاعتبار أن CVE-2022-32845 ليست مطلوبة للوصول إلى مسار الكود القابل للاستغلال من صندوق حماية التطبيق الافتراضي، بل يكفي ترجمة mlmodel مخصص لتحقيق نفس النتائج. يمكنك العثور على مزيد من التفاصيل حول CVE-2022-32845 في شرائح العرض التقديمي الخاصة بي.
ZinComputeProgramUpdateMutables() هي دالة أخرى يتم استدعاؤها بواسطة H11ANEIn::patchMutableSurface() وموجز الدالة (function prototype) كما يلي:
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 الذي يحتوي على مدخلات متسلسلة (serialized input)، يمكنك العثور على دالة التسلسل serialize_initinfo_section() في الكود المصدري لاستغلال weightBufs. mutable_procedure_info: هو مخزن IOSurface مشترك يوفره المهاجم، ويُسمى أيضاً weightsBuffer في استغلال weightBufs. mut_procedure_info_size: يشير إلى حجم مخزن السطح (surface buffer) mutable_procedure_info. MUTK_kernel_section: (أو MUTK) هو مخزن تعيين (mapping buffer) لكائن IOSurface يتم إنشاؤه بواسطة النواة أثناء مرحلة تحميل البرنامج. MUTK_kernel_section_size: هو حجم قسم النواة القابل للتعديل (mutable kernel section).

الحلقة 88-92 تحسب عدد كائنات MutableWeight داخل كائن mutable_procedure_info، ثم تحسب حجم التخصيص لمصفوفة MutableWeight عند السطر 93. بعد ذلك، يتم تخصيص مصفوفة كائنات ANECMutableWeight عند السطر 100، ثم يتم تعبئتها بزوج مخزن/حجم الوزن المناسب في الحلقة 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;
}
الكود الزائف (pseudo-code) للدالة 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;
}
الكود الزائف (pseudo-code) للدالة 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 في شرائح "مهاجمة المحرك العصبي لشركة Apple"، فلا تتردد في قراءتها إذا لم تكن قد فعلت ذلك بالفعل. يمكن العثور على تعريف البنية في استغلال weightBufs في 'aneProgram.h'.
إذا كنت قد لاحظت، فإن ANECGetMutableOperationInfo()->op_count يتم جلبه (fetched) مرتين: مرة لحساب الحجم من أجل تخصيص مصفوفة ANECMutableWeight، ومرة لملء هذه المصفوفة.
نظراً لأن مخزن mutable_procedure_info هو ذاكرة مشتركة، يمكن للمهاجم استخدام مؤشر ترابط منفصل (separate thread) لتغيير قيمة opsInfo->op_count بين الاستخدام الأول والثاني، مما يؤدي إلى عدم تطابق في الحجم سينتج عنه كتابة خارج النطاق (OOB write) مثيرة للاهتمام إما في منطقة kalloc var zone أو kheap defaul أو خريطة النواة (kernel map).
يمكن استخدام هذه الثغرة بعدة طرق مثيرة للاهتمام. على سبيل المثال، يمكن للمهاجم تعيين total_count = 0x1000; عند السطر 93، ثم زيادة opsInfo->count إلى قيمة أكبر، مما يتسبب في نسخ بيانات خارج الحدود في ANECGetMutableWeight().
ستتعطل النواة (kernel panic) عند التعليمات الموضحة أدناه إذا وصلت الكتابة خارج النطاق إلى منطقة ذاكرة غير معيّنة (unmapped memory area):
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
يوفر هذا الخطأ بدائية قوية (strong primitive) حيث يكتب قيمتين 64-بت: عنوان نواة يشير إلى المخزن المشترك الخاص بنا في وضع المستخدم وقيمة 64-بت (شبه) عشوائية.
يُترك إثبات المفهوم كتمرين للقارئ. ومع ذلك، فإن استغلال weightBufs يتضمن كل ما هو مطلوب للوصول إلى مسار الكود القابل للاستغلال. حظاً موفقاً :-).