
CVE-2026-74945, Divulgação de heap não inicializado por meio de uma fonte web especialmente criada (sec-high)
Este diretório contém um pacote de reprodução honesto para uma divulgação de
memória não inicializada no tratamento de fontes do Firefox. Uma fonte @font-face criada
leva o fluxo de saída do OTS até uma lacuna de heap não inicializado, e conteúdo comum lê bytes
dessa lacuna de volta por meio da API measureText do canvas. Ele foi feito para ser executado contra
uma build vulnerável do Firefox baixada localmente com dados sintéticos apenas. Ele
nunca toca qualquer sistema ativo ou de terceiros.
Repositório PoC correspondente: https://github.com/defineid/Palimpsest
Writeup: writeup-font-cmap-leak.html neste portfólio.
| Campo | Valor |
|---|---|
| CVE | CVE-2026-74945 |
| Componente | Core / Graphics: Text |
| Classe | Divulgação de Memória Não Inicializada (CWE-908 · CWE-200) |
| Severidade | sec-high (divulgação de memória acessível pela web) |
| Corrigido em | Firefox 154 · ESR 140.14 |
| Bug | bugzilla.mozilla.org/show_bug.cgi?id=2057808 |
| Interação | Nenhuma · apenas carregamento de página |
O Firefox sanitiza fontes web por meio do OTS antes do uso. Três defeitos independentes se alinham para que uma fonte criada deixe uma lacuna de heap não inicializado dentro da saída sanitizada e o conteúdo possa lê-la de volta:
moz_xrealloc) que não preenche com zeros o novo espaço.cmap de formato 14 com um
nonDefaultUVSOffset grande faz o OTS avançar seu fluxo de saída quando
reserializa a fonte sanitizada. O trecho ignorado entre os bytes escritos
e o alvo do seek é entregue ao chamador carregando heap não inicializado, e
forget() dimensiona o buffer até o maior offset alcançado.cmap em vez da
subtable, então um segmento cujo idRangeOffset ultrapassa o fim dos dados
do formato 4 ainda passa na verificação e lê os bytes não inicializados da subtable
vizinha.A fonte criada combina uma subtable de formato 4, cujo último segmento (0xFFFF) usa um
idRangeOffset ímpar e que ultrapassa os limites, com uma subtable de formato 14 que carrega um
nonDefaultUVSOffset grande, além de maxp.numGlyphs = 65535 e um avanço hmtx
distinto por id de glifo (avanço == id do glifo). Medir U+FFFF, portanto, resolve
para um id de glifo lido da lacuna não inicializada, e sua largura de avanço, legível
a partir de script, divulga cerca de dois bytes de heap por medição em uma janela de aproximadamente
64 KB.
Uma página web comum serve a fonte criada por meio de @font-face e chama
ctx.measureText(""). Nenhum clique, permissão, extensão ou alteração de pref é
necessário. O OTS é executado no processo e não está entre as bibliotecas em sandbox, então os
bytes divulgados são heap do processo de conteúdo que pode conter ponteiros ou segredos.
| Arquivo | Finalidade |
|---|---|
make_font.py | Cria leak.otf e leak.woff, as fontes criadas. |
trigger.html | Página simples que carrega a fonte via @font-face e lê o avanço de U+FFFF de volta por meio de 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
Em seguida, abra trigger.html na build vulnerável, a partir de um caminho file:// ou de um servidor estático local sob seu controle, e clique em Measure.
.notdef) e o avanço é 0. Nenhum gap é vazado, porque o fluxo de saída é zerado e a consulta de formato 4 é limitada à sua própria subtable.make_font.py foi executado aqui com fontTools 4.63.0 e produziu leak.otf (393816 bytes) e leak.woff (132372 bytes). A fonte gerada foi reprocessada a partir dos bytes brutos e cada ingrediente construído foi confirmado: maxp.numGlyphs = 65535, hhea.numberOfHMetrics = 65535, um avanço hmtx distinto por id de glifo (avanço == id de glifo), duas subtables cmap (plataforma 3 codificação 1 formato 4, e plataforma 0 codificação 5 formato 14), o último segmento do formato 4 endCode = 0xFFFF com um idRangeOffset = 0x4001 ímpar que ultrapassa o limite, e o nonDefaultUVSOffset = 0x8000 do formato 14.trigger.html você mesmo contra uma build vulnerável local para observar o vazamento. Este pacote demonstra a entrada construída e o caminho de leitura de volta; não é um exploit armado e não inclui nenhuma primitiva controlada de execução de código.Isto tem como alvo uma build baixada localmente com dados de fonte sintéticos e autogerados, para um relato do próprio CVE reportado e já corrigido pelo autor. Não ataca nenhum serviço ativo ou de terceiros.