
CVE-2022-22629 Prueba de concepto
Esta publicación trata sobre el PoC del bug de WebGL que se corrigió en las actualizaciones de seguridad de Safari 15.4.

Si quieres aprender en detalle cómo funciona WebGL, puedes usar este recurso oficial, ya que aquí vamos a discutir los detalles específicos del bug:
https://webglfundamentals.org/webgl/lessons/webgl-how-it-works.html
Este PoC no trata sobre cómo obtener las primitivas para explotar este bug; solo discutiremos el análisis de vulnerabilidad y cómo hacer fallar el contenido web : )
WebGL es una API de JavaScript que se utiliza en los navegadores para renderizar gráficos 2D y 3D. Utiliza el componente ANGLE como su backend principal de WebGL, mejorando significativamente la compatibilidad con otros navegadores y habilitando un WebGL 2.0 conforme en WebKit. WebGL ofrece muchas extensiones para que un desarrollador realice el renderizado con optimizaciones. Así que veremos la extensión que nos concierne.

Entonces, esta API proporciona varias extensiones para diferentes propósitos. El bug que vamos a discutir está en EXT_.
Puedes leer más en detalle sobre las extensiones desde aquí:
https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Using_Extensions
De todos modos, avancemos hacia el bug :)
La extensión WEBGL_multi_draw es parte de la API WebGL y permite renderizar más de una primitiva con una sola llamada a función. Esto puede mejorar el rendimiento de una aplicación WebGL, ya que reduce los costos de vinculación en el renderizador y acelera el tiempo del subproceso de la GPU con datos uniformes.
Esta extensión expone las variantes de llamadas de dibujo
Multi*en la funcionalidadEXT_multi_draw_arrays, además del builtingl_DrawIDdel shader de vértices expuesto porARB_shader_draw_parameterspara OpenGL.Estas funciones se comportan de manera idéntica a las funciones estándar
DrawArrays()yDrawElements(), excepto que manejan múltiples listas de vértices en una sola llamada. Su propósito principal es permitir que una llamada a función renderice más de una primitiva, como tiras de triángulos, abanicos de triángulos, etc.Además, esta extensión agrega una variable integrada adicional,
gl_DrawID, al lenguaje de sombreado. Esta variable contiene el índice del dibujo que está siendo procesado actualmente por una varianteMulti*de un comando de dibujo.
Puedes leer más detalles sobre la extensión aquí:
https://registry.khronos.org/webgl/extensions/WEBGL_multi_draw/
Esta extensión es similar a la API de WebGL WebGLRenderingContext.drawArrays() y WebGLRenderingContext.drawElements(), etc., que renderizan primitivas a partir de datos de arreglos. La única diferencia es que podemos renderizar usando esta extensión con múltiples arreglos.
Podemos realizar estas operaciones usando esta extensión: [1]
ext.multiDrawArraysWEBGL() renderiza múltiples primitivas a partir de datos de arreglos (idéntico a múltiples llamadas a drawArrays).
ext.multiDrawElementsWEBGL() renderiza múltiples primitivas a partir de datos de arreglos de elementos (idéntico a múltiples llamadas a drawElements).
ext.multiDrawArraysInstancedWEBGL() renderiza múltiples primitivas a partir de datos de arreglos (idéntico a múltiples llamadas a drawArraysInstanced).
ext.multiDrawElementsInstancedWEBGL() renderiza múltiples primitivas a partir de datos de arreglos de elementos (idéntico a múltiples llamadas a drawElementsInstanced).

https://github.com/WebKit/WebKit/commit/c087ca07c9125093ca62e8e44024b738bb7d46dc
La vulnerabilidad existe solo desde Safari 15.0 hasta 15.3, porque esta extensión solo es compatible con Safari 15 (iOS) en el caso de Apple.
El parche es muy fácil de entender y claramente indica que se trata de un desbordamiento de búfer en el montón durante el renderizado. Esta función se llama aquí en WebGLMultiDraw.cpp en todas las operaciones que mencionamos anteriormente [1]. Entendamos qué está sucediendo antes de que se llame a esta función. Podemos ver la implementación de una de las funciones anteriores:
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);
}
En JS, las especificaciones de la función se definen así:
void ext.multiDrawArraysWEBGL(mode,
firstsList, firstsOffset,
countsList, countsOffset,
drawCount);
mode:
gl.POINTS: Dibuja un solo punto.gl.TRIANGLES: Dibuja un triángulo para un grupo de tres vértices.firstsList:
firstsOffset:
countsList:
countsOffset:
drawCount:
Mirando la implementación, podemos ver que podemos controlar el offset y el drawcount para dibujar más allá del tamaño del búfer : )
Entonces, nuestro PoC está aquí ...

Como puedes ver, nuestra página web simplemente falló, ya que validateOffset() llamado en multiDrawArraysWEBGL() no verifica si nuestro firstsOffset está en el rango de [0, size-drawcount] y estamos dibujando más allá del tamaño del búfer real. La llamada a la función m_context->graphicsContextGL()->multiDrawArraysANGLE() está implementada en los componentes de terceros para WebKit conocidos como ANGLE y no tiene ninguna verificación antes de renderizar, por lo que provocamos un fallo al escribir algún valor incorrecto en la región de memoria.
Para tu información, el pseudocódigo de multiDrawArraysANGLE() funciona así:
for(i=0; i<drawcount; i++) {
if (*(counts+i)>0) DrawArrays(mode, *(firsts+i), *(counts+i));
}
Y con el parche aplicado, no renderizará nada y devolverá falso a través de la llamada a la función validateOffset():

js) ...
Equipo de PARS Defense
Estamos contratando nuevos miembros para el equipo, contáctanos: info[at]parsdefense.com