
CVE-2022-22629 Proof of Concept
In diesem Beitrag geht es um den PoC für den WebGL-Bug, der in den Sicherheitsupdates von Safari 15.4 behoben wurde.

Wenn du im Detail lernen möchtest, wie WebGL funktioniert, kannst du diese offizielle Ressource nutzen, da wir hier bugspezifische Details besprechen:
https://webglfundamentals.org/webgl/lessons/webgl-how-it-works.html
Bei diesem PoC geht es nicht darum, wie man die Primitiven erhält, um diesen Bug auszunutzen. Wir besprechen lediglich die Schwachstellenanalyse und wie man den WebContent-Prozess zum Absturz bringt : )
WebGL ist eine JavaScript-API, die in Browsern verwendet wird, um 2D- und 3D-Grafiken zu rendern. Sie verwendet die ANGLE-Komponente als primäres WebGL-Backend, was die Kompatibilität mit anderen Browsern erheblich verbessert und ein konformes WebGL 2.0 in WebKit ermöglicht. WebGL bietet Entwicklern viele Erweiterungen, um das Rendering mit Optimierungen durchzuführen. Also schauen wir uns die Erweiterung an, die uns betrifft.

Diese API bietet also verschiedene Erweiterungen für unterschiedliche Zwecke. Der Bug, den wir besprechen werden, befindet sich in EXT_.
Weitere Details zu den Erweiterungen findest du hier:
https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Using_Extensions
Wie auch immer, kommen wir zum Bug :)
Die WEBGL_multi_draw-Erweiterung ist Teil der WebGL-API und ermöglicht es, mit einem einzigen Funktionsaufruf mehr als ein Primitiv zu rendern. Dies kann die Leistung einer WebGL-Anwendung verbessern, da es die Bindingskosten im Renderer reduziert und die GPU-Thread-Zeit bei uniformen Daten beschleunigt.
Diese Erweiterung stellt die
Multi*-Draw-Call-Varianten derEXT_multi_draw_arrays-Funktionalität zusätzlich zu dem vonARB_shader_draw_parametersfür OpenGL bereitgestellten Vertex-Shader-Builtingl_DrawIDbereit.Diese Funktionen verhalten sich identisch zu den Standardfunktionen
DrawArrays()undDrawElements(), außer dass sie mehrere Listen von Vertices in einem Aufruf verarbeiten. Ihr Hauptzweck besteht darin, mit einem Funktionsaufruf mehr als ein Primitiv wie Triangle Strip, Triangle Fan usw. zu rendern.Darüber hinaus fügt diese Erweiterung der Shading-Sprache eine weitere eingebaute Variable hinzu,
gl_DrawID. Diese Variable enthält den Index des Draws, der derzeit von einerMulti*-Variante eines Zeichenbefehls verarbeitet wird.
Weitere Details zu dieser Erweiterung findest du hier:
https://registry.khronos.org/webgl/extensions/WEBGL_multi_draw/
Diese Erweiterung ähnelt der WebGL-API WebGLRenderingContext.drawArrays() und WebGLRenderingContext.drawElements() usw., die Primitiven aus Array-Daten rendert. Der einzige Unterschied besteht darin, dass wir mit dieser Erweiterung mehrere Arrays zum Rendern verwenden können.
Mit dieser Erweiterung können wir die folgenden Operationen durchführen: [1]
ext.multiDrawArraysWEBGL() rendert mehrere Primitiven aus Array-Daten (identisch zu mehreren Aufrufen von drawArrays).
ext.multiDrawElementsWEBGL() rendert mehrere Primitiven aus Element-Array-Daten (identisch zu mehreren Aufrufen von drawElements).
ext.multiDrawArraysInstancedWEBGL() rendert mehrere Primitiven aus Array-Daten (identisch zu mehreren Aufrufen von drawArraysInstanced).
ext.multiDrawElementsInstancedWEBGL() rendert mehrere Primitiven aus Element-Array-Daten (identisch zu mehreren Aufrufen von drawElementsInstanced).

https://github.com/WebKit/WebKit/commit/c087ca07c9125093ca62e8e44024b738bb7d46dc
Die Schwachstelle existiert nur von Safari 15.0 bis 15.3, da diese Erweiterung bei Apple nur in Safari 15 (iOS) unterstützt wird.
Der Patch ist sehr leicht zu verstehen und zeigt deutlich, dass es sich um einen Heap-Buffer-Overflow beim Rendern handelt. Diese Funktion wird hier in WebGLMultiDraw.cpp bei allen oben genannten Operationen aufgerufen [1]. Lass uns verstehen, was passiert, bevor diese Funktion aufgerufen wird. Schauen wir uns die Implementierung einer der obigen Funktionen an:
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 JavaScript sind die Funktionsspezifikationen wie folgt definiert:
void ext.multiDrawArraysWEBGL(mode,
firstsList, firstsOffset,
countsList, countsOffset,
drawCount);
mode:
gl.POINTS: Zeichnet einen einzelnen Punkt.gl.TRIANGLES: Zeichnet ein Dreieck für eine Gruppe von drei Vertices.firstsList:
firstsOffset:
countsList:
countsOffset:
drawCount:
Wenn wir uns die Implementierung ansehen, sehen wir, dass wir den Offset und den Drawcount steuern können, um über die Puffergröße hinaus zu zeichnen : )
Unser PoC befindet sich also hier ...

Wie du siehst, ist unsere Webseite einfach abgestürzt, da das in multiDrawArraysWEBGL() aufgerufene validateOffset() nicht prüft, ob unser firstsOffset im Bereich [0, size-drawcount] liegt, und wir über die Größe des tatsächlichen Puffers hinaus zeichnen. Der Funktionsaufruf m_context->graphicsContextGL()->multiDrawArraysANGLE() ist in Drittanbieterkomponenten für WebKit implementiert, die als ANGLE bekannt sind, und enthält vor dem Rendern keine Prüfungen. Daher stürzen wir ab, während wir einen ungültigen Wert in den Speicherbereich schreiben.
Nur zur Info, der Pseudocode von multiDrawArraysANGLE() funktioniert so:
for(i=0; i<drawcount; i++) {
if (*(counts+i)>0) DrawArrays(mode, *(firsts+i), *(counts+i));
}
Mit angewendetem Patch wird nichts gerendert und über den Funktionsaufruf validateOffset() false zurückgegeben:

js) ...
PARS Defense Team
Wir suchen neue Teammitglieder, kontaktiere uns: info[at]parsdefense.com