
CVE-2022-22629 के लिए प्रूफ ऑफ कॉन्सेप्ट, Safari के WebGL मल्टी-ड्रॉ एक्सटेंशन में एक हीप बफर ओवरफ्लो, जिसमें भेद्यता विश्लेषण और क्रैश पुनरुत्पादन चरण शामिल हैं।
यह पोस्ट उस WebGL बग के poc के बारे में है जिसे Safari 15.4 सुरक्षा अपडेट में पैच किया गया था।

अगर आप विस्तार से जानना चाहते हैं कि WebGL कैसे काम करता है, तो आप इस आधिकारिक संसाधन का उपयोग कर सकते हैं, क्योंकि हम यहाँ बग से संबंधित विशेष विवरणों पर चर्चा करने जा रहे हैं:
https://webglfundamentals.org/webgl/lessons/webgl-how-it-works.html
यह poc इस बारे में नहीं है कि इस बग का शोषण करने के लिए प्रिमिटिव कैसे प्राप्त करें, हम केवल भेद्यता विश्लेषण और webcontent को क्रैश करने के तरीके पर चर्चा करेंगे : )
WebGL एक जावास्क्रिप्ट API है जिसका उपयोग ब्राउज़रों में 2D और 3D ग्राफिक्स रेंडर करने के लिए किया जाता है। यह अपने प्राथमिक WebGL बैकएंड के रूप में ANGLE घटक का उपयोग करता है, जो अन्य ब्राउज़रों के साथ संगतता में काफी सुधार करता है, और WebKit में एक अनुरूप WebGL 2.0 सक्षम करता है। WebGL डेवलपर के लिए अनुकूलन के साथ रेंडरिंग करने हेतु बहुत सारे एक्सटेंशन प्रदान करता है। तो, हम उस एक्सटेंशन को देखेंगे जिससे हमारा सरोकार है।

तो, यह API विभिन्न उद्देश्यों के लिए कई एक्सटेंशन प्रदान करता है। जिस बग पर हम चर्चा करने वाले हैं वह EXT_ में है।
आप एक्सटेंशन के बारे में यहाँ से अधिक विस्तार से पढ़ सकते हैं:
खैर, चलिए बग की ओर बढ़ते हैं :)
WEBGL_multi_draw एक्सटेंशन WebGL API का हिस्सा है और एक ही फ़ंक्शन कॉल के साथ एक से अधिक प्रिमिटिव रेंडर करने की अनुमति देता है। यह WebGL एप्लिकेशन के प्रदर्शन को बेहतर कर सकता है क्योंकि यह रेंडरर में बाइंडिंग लागत को कम करता है और यूनिफ़ॉर्म डेटा के साथ GPU थ्रेड समय को तेज़ करता है।
यह एक्सटेंशन OpenGL के लिए
ARB_shader_draw_parametersद्वारा उजागर किए गए vertex shader बिल्टिनgl_DrawIDके अतिरिक्त,EXT_multi_draw_arraysकार्यक्षमता मेंMulti*ड्रॉ कॉल वेरिएंट को उजागर करता है।ये फ़ंक्शन मानक फ़ंक्शन
DrawArrays()औरDrawElements()के समान व्यवहार करते हैं, सिवाय इसके कि वे एक कॉल में वर्टिस की कई सूचियों को संभालते हैं। इनका मुख्य उद्देश्य एक फ़ंक्शन कॉल को एक से अधिक प्रिमिटिव जैसे triangle strip, triangle fan, आदि रेंडर करने की अनुमति देना है।इसके अतिरिक्त, यह एक्सटेंशन shading language में एक और बिल्टिन वेरिएबल,
gl_DrawIDजोड़ता है। यह वेरिएबल उस ड्रॉ का इंडेक्स रखता है जिसे वर्तमान में ड्रॉइंग कमांड केMulti*वेरिएंट द्वारा संसाधित किया जा रहा है।
आप इस एक्सटेंशन के बारे में अधिक विवरण यहाँ पढ़ सकते हैं:
यह एक्सटेंशन WebGL API के WebGLRenderingContext.drawArrays() और WebGLRenderingContext.drawElements() आदि के समान है, जो ऐरे डेटा से प्रिमिटिव रेंडर करते हैं। एकमात्र अंतर यह है कि हम इस एक्सटेंशन का उपयोग करके कई ऐरे के साथ रेंडर कर सकते हैं।
हम इस एक्सटेंशन का उपयोग करके ये ऑपरेशन कर सकते हैं: [1]
ext.multiDrawArraysWEBGL() ऐरे डेटा से कई प्रिमिटिव रेंडर करता है (drawArrays के कई कॉल्स के समान)।
ext.multiDrawElementsWEBGL() एलिमेंट ऐरे डेटा से कई प्रिमिटिव रेंडर करता है (drawElements के कई कॉल्स के समान)।
ext.multiDrawArraysInstancedWEBGL() ऐरे डेटा से कई प्रिमिटिव रेंडर करता है (drawArraysInstanced के कई कॉल्स के समान)।
ext.multiDrawElementsInstancedWEBGL() एलिमेंट ऐरे डेटा से कई प्रिमिटिव रेंडर करता है (drawElementsInstanced के कई कॉल्स के समान)।

यह भेद्यता केवल Safari 15.0 से 15.3 तक मौजूद है, क्योंकि Apple के मामले में यह एक्सटेंशन केवल Safari 15 (iOS) में समर्थित है।
पैच को समझना बहुत आसान है और यह स्पष्ट रूप से बताता है कि यह रेंडरिंग के दौरान heap buffer overflow है। यह फ़ंक्शन ऊपर [1] में बताए गए सभी ऑपरेशनों में WebGLMultiDraw.cpp के अंदर कॉल किया जाता है। आइए समझते हैं कि इस फ़ंक्शन के कॉल होने से पहले क्या हो रहा है। हम ऊपर दिए गए किसी एक फ़ंक्शन के कार्यान्वयन को देख सकते हैं:
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:
कार्यान्वयन को देखने पर, हम देख सकते हैं कि हम offset और drawcount को नियंत्रित कर सकते हैं ताकि बफर के आकार से आगे draw mode तक ड्रॉ किया जा सके : )
तो, हमारा POC यहाँ है ...

जैसा कि आप देख सकते हैं, हमारा वेबपेज क्रैश हो गया क्योंकि multiDrawArraysWEBGL() में कॉल किया गया validateOffset() यह जाँच नहीं करता कि हमारा firstsOffset [0, size-drawcount] की सीमा में है, और हम वास्तविक बफर के आकार से आगे ड्रॉ कर रहे हैं। m_context->graphicsContextGL()->multiDrawArraysANGLE() फ़ंक्शन कॉल WebKit के लिए तृतीय-पक्ष घटकों में लागू किया गया है, जिसे ANGLE के नाम से जाना जाता है, और यह रेंडरिंग से पहले कोई जाँच नहीं करता है, इसलिए हम मेमोरी क्षेत्र में कुछ गलत मान लिखकर क्रैश कर देते हैं।
FYI, multiDrawArraysANGLE() का स्यूडोकोड इस प्रकार काम करता है:
for(i=0; i<drawcount; i++) {
if (*(counts+i)>0) DrawArrays(mode, *(firsts+i), *(counts+i));
}
और पैच लागू होने के बाद, यह कुछ भी रेंडर नहीं करेगा और validateOffset() फ़ंक्शन कॉल के माध्यम से false लौटाएगा:

js) ...
PARS Defense Team
हम नए टीम सदस्यों को नियुक्त कर रहे हैं, संपर्क करें: info[at]parsdefense.com