
CVE-2022-22629 Prova di concetto
Questo post riguarda la PoC per il bug WebGL che è stato corretto negli aggiornamenti di sicurezza di Safari 15.4.

Se vuoi imparare in dettaglio come funziona WebGL, puoi usare questa risorsa ufficiale, dato che qui discuteremo i dettagli specifici del bug:
https://webglfundamentals.org/webgl/lessons/webgl-how-it-works.html
Questa PoC non riguarda come ottenere le primitive per sfruttare questo bug: discuteremo solo l'analisi della vulnerabilità e come mandare in crash il webcontent : )
WebGL è un'API JavaScript utilizzata nei browser per renderizzare grafica 2D e 3D. Usa il componente ANGLE come backend WebGL principale, migliorando significativamente la compatibilità con altri browser e consentendo un WebGL 2.0 conforme in WebKit. WebGL offre molte estensioni per consentire a uno sviluppatore di eseguire il rendering con ottimizzazioni. Quindi, esamineremo l'estensione che ci interessa.

Quindi, questa API fornisce diverse estensioni per vari scopi. Il bug che discuteremo è in EXT_.
Puoi leggere più in dettaglio le estensioni qui:
https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Using_Extensions
Comunque, passiamo al bug :)
L'estensione WEBGL_multi_draw fa parte dell'API WebGL e consente di renderizzare più di una primitiva con una singola chiamata di funzione. Questo può migliorare le prestazioni di un'applicazione WebGL poiché riduce i costi di binding nel renderer e accelera il tempo del thread GPU con dati uniformi.
Questa estensione espone le varianti di chiamata di disegno
Multi*nella funzionalitàEXT_multi_draw_arrays, oltre al built-in dello shader vertexgl_DrawIDesposto daARB_shader_draw_parametersper OpenGL.Queste funzioni si comportano in modo identico alle funzioni standard
DrawArrays()eDrawElements()tranne per il fatto che gestiscono più elenchi di vertici in un'unica chiamata. Il loro scopo principale è consentire a una singola chiamata di funzione di renderizzare più di una primitiva, come triangle strip, triangle fan, ecc.Inoltre, questa estensione aggiunge un'ulteriore variabile integrata,
gl_DrawID, al linguaggio di shading. Questa variabile contiene l'indice del disegno attualmente processato da una varianteMulti*di un comando di disegno.
Puoi leggere ulteriori dettagli sull'estensione qui:
https://registry.khronos.org/webgl/extensions/WEBGL_multi_draw/
Questa estensione è simile all'API WebGL WebGLRenderingContext.drawArrays() e WebGLRenderingContext.drawElements(), ecc., che renderizza primitive da dati in array. L'unica differenza è che con questa estensione possiamo renderizzare usando più array.
Possiamo eseguire queste operazioni usando questa estensione: [1]
ext.multiDrawArraysWEBGL() renderizza più primitive da dati in array (identico a più chiamate a drawArrays).
ext.multiDrawElementsWEBGL() renderizza più primitive da dati di array di elementi (identico a più chiamate a drawElements).
ext.multiDrawArraysInstancedWEBGL() renderizza più primitive da dati in array (identico a più chiamate a drawArraysInstanced).
ext.multiDrawElementsInstancedWEBGL() renderizza più primitive da dati di array di elementi (identico a più chiamate a drawElementsInstanced).

https://github.com/WebKit/WebKit/commit/c087ca07c9125093ca62e8e44024b738bb7d46dc
La vulnerabilità esiste solo da Safari 15.0 a 15.3 perché questa estensione è supportata solo in Safari 15 (iOS) nel caso di Apple.
La patch è molto facile da capire e afferma chiaramente che si tratta di un heap buffer overflow durante il rendering. Questa funzione viene chiamata qui in WebGLMultiDraw.cpp in tutte le operazioni che abbiamo menzionato sopra [1]. Cerchiamo di capire cosa succede prima che questa funzione venga chiamata. Possiamo guardare l'implementazione di una delle funzioni sopra:
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);
}
In JS, le specifiche della funzione sono definite così:
void ext.multiDrawArraysWEBGL(mode,
firstsList, firstsOffset,
countsList, countsOffset,
drawCount);
mode:
gl.POINTS: Disegna un singolo punto.gl.TRIANGLES: Disegna un triangolo per un gruppo di tre vertici.firstsList:
firstsOffset:
countsList:
countsOffset:
drawCount:
Guardando l'implementazione, possiamo vedere che possiamo controllare l'offset e il drawcount per disegnare oltre la dimensione del buffer : )
Quindi, la nostra PoC si trova qui ...

Come puoi vedere, la nostra pagina web è andata in crash poiché validateOffset() chiamato in multiDrawArraysWEBGL() non verifica se il nostro firstsOffset è nell'intervallo [0, size-drawcount] e stiamo disegnando oltre la dimensione del buffer effettivo. La chiamata di funzione m_context->graphicsContextGL()->multiDrawArraysANGLE() è implementata nei componenti di terze parti per WebKit noti come ANGLE e non ha alcun controllo prima del rendering, quindi andiamo in crash scrivendo un valore errato nella regione di memoria.
Per tua informazione, lo pseudocodice di multiDrawArraysANGLE() funziona così:
for(i=0; i<drawcount; i++) {
if (*(counts+i)>0) DrawArrays(mode, *(firsts+i), *(counts+i));
}
E con la patch applicata, non renderizzerà nulla e restituirà false tramite la chiamata di funzione validateOffset():

js) ...
PARS Defense Team
Stiamo assumendo nuovi membri del team, contattaci: info[at]parsdefense.com