
CVE-2026-74945, divulgation de tas non initialisé via une police web malveillante (sec-high)
Ce répertoire contient un paquet de reproduction honnête pour une divulgation de mémoire non initialisée dans la gestion des polices de Firefox. Une police @font-face forgée entraîne le flux de sortie d'OTS dans une zone de tas non initialisé, et un contenu ordinaire relit les octets de cette zone via l'API canvas measureText. Il est destiné à être exécuté contre une version vulnérable de Firefox téléchargée localement avec des données synthétiques uniquement. Il ne touche jamais aucun système actif ou tiers.
Dépôt PoC correspondant : https://github.com/defineid/Palimpsest
Analyse : writeup-font-cmap-leak.html dans ce portfolio.
| Champ | Valeur |
|---|---|
| CVE | CVE-2026-74945 |
| Composant | Core / Graphics: Text |
| Classe | Divulgation de mémoire non initialisée (CWE-908 · CWE-200) |
| Gravité | sec-high (divulgation de mémoire accessible depuis le web) |
| Corrigé dans | Firefox 154 · ESR 140.14 |
| Bug | bugzilla.mozilla.org/show_bug.cgi?id=2057808 |
| Interaction | Aucune · chargement de page uniquement |
Firefox assainit les polices web via OTS avant utilisation. Trois défauts indépendants s'alignent pour qu'une police forgée laisse une zone de tas non initialisé dans la sortie assainie et que le contenu puisse la relire :
moz_xrealloc) qui ne remplit pas de zéros le nouvel espace.cmap de format 14 avec un grand nonDefaultUVSOffset amène OTS à déplacer son flux de sortie vers l'avant lorsqu'il re-sérialise la police assainie. L'intervalle sauté entre les octets écrits et la cible du seek est remis à l'appelant en portant du tas non initialisé, et forget() dimensionne le tampon jusqu'au décalage le plus élevé atteint.cmap entière plutôt qu'à la sous-table, de sorte qu'un segment dont le idRangeOffset dépasse la fin des données de format 4 passe quand même la vérification et lit dans les octets non initialisés de la sous-table voisine.La police forgée associe une sous-table de format 4, dont le dernier segment (0xFFFF) utilise un idRangeOffset impair et débordant, à une sous-table de format 14 portant un grand nonDefaultUVSOffset, plus maxp.numGlyphs = 65535 et une avance hmtx distincte par identifiant de glyphe (avance == identifiant de glyphe). Mesurer U+FFFF résout donc vers un identifiant de glyphe lu dans la zone non initialisée, et sa largeur d'avance, lisible depuis un script, divulgue environ deux octets de tas par mesure sur une fenêtre d'environ 64 Ko.
Une page web ordinaire sert la police forgée via @font-face et appelle ctx.measureText(""). Aucun clic, permission, extension ou changement de préférence n'est requis. OTS s'exécute dans le processus et ne fait pas partie des bibliothèques sandboxées, donc les octets divulgués sont du tas du processus de contenu qui peut contenir des pointeurs ou des secrets.
| Fichier | Objectif |
|---|---|
make_font.py | Construit leak.otf et leak.woff, les polices forgées. |
trigger.html | Page minimale qui charge la police via @font-face et relit l'avance de U+FFFF via measureText. |
https://ftp.mozilla.org/pub/firefox/releases/140.9.0esr/.pip install fonttools brotli
python3 make_font.py # writes leak.otf and leak.woff in this directory
Ouvrez ensuite trigger.html dans la version vulnérable, depuis un chemin file:// ou un serveur statique local que vous contrôlez, puis cliquez sur Measure.
.notdef) et l'avance vaut 0. Aucun espace n'est divulgué, car le flux de sortie est mis à zéro et la recherche de format 4 est limitée à sa propre sous-table.make_font.py a été exécuté ici avec fontTools 4.63.0 et a produit leak.otf (393816 octets) et leak.woff (132372 octets). La police générée a été reparsée à partir des octets bruts et chaque ingrédient fabriqué a été confirmé : maxp.numGlyphs = 65535, hhea.numberOfHMetrics = 65535, une avance hmtx distincte par identifiant de glyphe (avance == identifiant de glyphe), deux sous-tables cmap (plateforme 3 encodage 1 format 4, et plateforme 0 encodage 5 format 14), le dernier segment de format 4 endCode = 0xFFFF avec un idRangeOffset = 0x4001 impair qui déborde, et le nonDefaultUVSOffset = 0x8000 du format 14.trigger.html vous-même contre une version vulnérable locale pour observer la fuite. Ce paquet démontre l'entrée fabriquée et le chemin de relecture ; ce n'est pas un exploit armé et il n'inclut aucune primitive contrôlée d'exécution de code.Ceci cible une version téléchargée localement avec des données de police synthétiques et auto-générées, pour un compte rendu de la CVE signalée par l'auteur lui-même et déjà corrigée. Cela n'attaque aucun service actif ni tiers.