
CVE-2022-22629 إثبات المفهوم
هذه المقالة تتعلق بإثبات المفهوم لثغرة WebGL التي تم تصحيحها في تحديثات أمان Safari 15.4.

إذا كنت ترغب في التعلم بالتفصيل عن كيفية عمل WebGL، يمكنك استخدام هذا المورد الرسمي حيث سنناقش تفاصيل الثغرة هنا:
https://webglfundamentals.org/webgl/lessons/webgl-how-it-works.html
إثبات المفهوم هذا ليس حول كيفية الحصول على الأوليات لاستغلال هذه الثغرة، سنناقش فقط تحليل الثغرة وكيفية تعطل محتوى الويب : )
WebGL هي واجهة برمجة تطبيقات جافا سكريبت تُستخدم في المتصفحات لعرض الرسوميات ثنائية وثلاثية الأبعاد. تستخدم مكون ANGLE كخلفية رئيسية لـ WebGL، مما يحسن التوافق مع المتصفحات الأخرى بشكل كبير، ويتيح WebGL 2.0 المتوافق في WebKit. يوفر WebGL العديد من الامتدادات للمطور لإجراء العرض مع تحسينات. لذا، سننظر في الامتداد الذي يهمنا.

لذا، توفر هذه الواجهة العديد من الامتدادات لأغراض مختلفة. الثغرة التي سنناقشها موجودة في EXT_.
يمكنك قراءة المزيد من التفاصيل حول الامتدادات من هنا:
https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Using_Extensions
على أي حال، دعنا نتجه نحو الثغرة :)
امتداد WEBGL_multi_draw هو جزء من واجهة WebGL ويسمح بعرض أكثر من شكل أولي باستدعاء دالة واحدة. يمكن أن يحسن ذلك أداء تطبيق WebGL حيث يقلل تكاليف الربط في المُقدم ويسرع وقت معالجة وحدة معالجة الرسومات مع البيانات الموحدة.
يكشف هذا الامتداد عن متغيرات استدعاء الرسم
Multi*في وظيفةEXT_multi_draw_arraysبالإضافة إلىgl_DrawIDالمدمج في تظليل الرأس المكشوف بواسطةARB_shader_draw_parametersلـ OpenGL.تتصرف هذه الدوال تمامًا مثل الدوال القياسية
DrawArrays()وDrawElements()إلا أنها تتعامل مع قوائم متعددة من الرؤوس في استدعاء واحد. والغرض الرئيسي منها هو السماح باستدعاء دالة واحدة لعرض أكثر من شكل أولي مثل شريط المثلثات، مروحة المثلثات، إلخ.بالإضافة إلى ذلك، يضيف هذا الامتداد متغيرًا مدمجًا آخر،
gl_DrawID، إلى لغة التظليل. يحتوي هذا المتغير على فهرس الرسم الجاري معالجته حاليًا بواسطة متغيرMulti*لأمر الرسم.
يمكنك قراءة المزيد من التفاصيل حول الامتداد هنا:
https://registry.khronos.org/webgl/extensions/WEBGL_multi_draw/
يشبه هذا الامتداد واجهة WebGL WebGLRenderingContext.drawArrays() و WebGLRenderingContext.drawElements() إلخ، التي تعرض الأشكال الأولية من بيانات المصفوفة. الاختلاف الوحيد هو أنه يمكننا العرض باستخدام هذا الامتداد باستخدام مصفوفات متعددة.
يمكننا إجراء هذه العمليات باستخدام هذا الامتداد: [1]
ext.multiDrawArraysWEBGL() لعرض أشكال أولية متعددة من بيانات المصفوفة (مطابق لاستدعاءات متعددة لـ drawArrays).
ext.multiDrawElementsWEBGL() لعرض أشكال أولية متعددة من بيانات مصفوفة العناصر (مطابق لاستدعاءات متعددة لـ drawElements).
ext.multiDrawArraysInstancedWEBGL() لعرض أشكال أولية متعددة من بيانات المصفوفة (مطابق لاستدعاءات متعددة لـ drawArraysInstanced).
ext.multiDrawElementsInstancedWEBGL() لعرض أشكال أولية متعددة من بيانات مصفوفة العناصر (مطابق لاستدعاءات متعددة لـ drawElementsInstanced).

https://github.com/WebKit/WebKit/commit/c087ca07c9125093ca62e8e44024b738bb7d46dc
الثغرة موجودة فقط من Safari 15.0 إلى 15.3 لأن هذا الامتداد مدعوم فقط في Safari 15 (ios) في حالة أبل.
التصحيح سهل الفهم ويوضح بوضوح أنه تجاوز سعة المخزن المؤقت في الكومة أثناء العرض. يتم استدعاء هذه الدالة هنا في WebGLMultiDraw.cpp في جميع العمليات المذكورة أعلاه [1]. دعنا نفهم ما يحدث قبل استدعاء هذه الدالة. قد ننظر إلى تنفيذ إحدى الدوال أعلاه:
void WebGLMultiDraw::multiDrawArraysWEBGL(GCGLenum mode, Int32List firstsList, GCGLuint firstsOffset, Int32List countsList, GCGLuint countsOffset, GCGLsizei drawcount)
{
if (!m_context || m_context->isContextLost())
return;
if (!validateDrawcount("multiDrawArraysWEBGL", drawcount)
|| !validateOffset("multiDrawArraysWEBGL", "firstsOffset out of bounds", firstsList.length(), firstsOffset, drawcount)
|| !validateOffset("multiDrawArraysWEBGL", "countsOffset out of bounds", countsList.length(), countsOffset, drawcount)) {
return;
}
m_context->graphicsContextGL()->multiDrawArraysANGLE(mode, makeSpanWithOffset(firstsList, firstsOffset), makeSpanWithOffset(countsList, countsOffset), drawcount);
}
في JS، يتم تعريف مواصفات الدالة هكذا:
void ext.multiDrawArraysWEBGL(mode,
firstsList, firstsOffset,
countsList, countsOffset,
drawCount);
mode:
gl.POINTS: يرسم نقطة واحدة.gl.TRIANGLES: يرسم مثلثًا لمجموعة من ثلاث رؤوس.firstsList:
firstsOffset:
countsList:
countsOffset:
drawCount:
بالنظر إلى التنفيذ، يمكننا أن نرى أنه يمكننا التحكم في الإزاحة وعدد الرسومات للرسم في وضع يتجاوز حجم المخزن المؤقت : )
لذا، إثبات المفهوم لدينا هو هنا ...

كما ترى، تعطلت صفحة الويب لدينا لأن validateOffset() التي تم استدعاؤها في multiDrawArraysWEBGL() لا تتحقق مما إذا كانت firstsOffset في النطاق [0, size-drawcount] ونحن نرسم بحجم يتجاوز حجم المخزن المؤقت الفعلي. يتم تنفيذ استدعاء الدالة m_context->graphicsContextGL()->multiDrawArraysANGLE() في مكونات طرف ثالث لـ WebKit تُعرف باسم ANGLE وليس لديها أي فحوصات قبل العرض، لذا نتعطل بكتابة قيمة سيئة إلى منطقة الذاكرة.
لعلمك، الكود الزائف لـ multiDrawArraysANGLE() يعمل هكذا:
for(i=0; i<drawcount; i++) {
if (*(counts+i)>0) DrawArrays(mode, *(firsts+i), *(counts+i));
}
ومع تطبيق التصحيح، لن يعرض أي شيء وسيعيد false من خلال استدعاء الدالة validateOffset():

js) ...
فريق PARS Defense
نحن نوظف أعضاء جدد في الفريق، اتصل بنا: info[at]parsdefense.com