
CVE-2026-74945, раскрытие неинициализированных данных кучи через специально сформированный веб-шрифт (sec-high)
Этот каталог содержит пакет для добросовестного воспроизведения раскрытия
неинициализированной памяти при обработке шрифтов в Firefox. Специально сформированный шрифт @font-face продвигает выходной поток OTS в область неинициализированной
кучи, и обычный контент считывает байты этой
области обратно через API canvas measureText. Пакет предназначен для запуска против
локально загруженной уязвимой сборки Firefox и только с синтетическими данными. Он
никогда не взаимодействует с какой-либо живой или сторонней системой.
Соответствующий репозиторий PoC: https://github.com/defineid/Palimpsest
Описание: writeup-font-cmap-leak.html в этом портфолио.
| Поле | Значение |
|---|---|
| CVE | CVE-2026-74945 |
| Компонент | Core / Graphics: Text |
| Класс | Раскрытие неинициализированной памяти (CWE-908 · CWE-200) |
| Серьёзность | sec-high (раскрытие памяти, достижимое из веба) |
| Исправлено в | Firefox 154 · ESR 140.14 |
| Баг | bugzilla.mozilla.org/show_bug.cgi?id=2057808 |
| Взаимодействие | Отсутствует · только загрузка страницы |
Firefox очищает веб-шрифты через OTS перед использованием. Три независимых дефекта складываются так, что специально сформированный шрифт оставляет область неинициализированной кучи внутри очищенного вывода, и контент может прочитать её обратно:
moz_xrealloc), который не заполняет новое пространство нулями.cmap формата 14 с
большим nonDefaultUVSOffset заставляет OTS перемещать свой выходной поток вперёд при
повторной сериализации очищенного шрифта. Пропущенный диапазон между записанными байтами
и целью seek передаётся вызывающему коду с неинициализированной кучей, а
forget() задаёт размер буфера по наибольшему достигнутому смещению.cmap, а не
подтаблицы, поэтому сегмент, чей idRangeOffset выходит за конец данных
формата 4, всё равно проходит проверку и читает в неинициализированные байты
соседней подтаблицы.Специально сформированный шрифт сочетает подтаблицу формата 4, последний сегмент которой (0xFFFF) использует
нечётный, выходящий за границы idRangeOffset, с подтаблицей формата 14, несущей большой
nonDefaultUVSOffset, плюс maxp.numGlyphs = 65535 и отдельное для каждого glyph id значение advance в hmtx
(advance == glyph id). Поэтому измерение U+FFFF разрешается
в glyph id, прочитанный из неинициализированной области, а его ширина advance, читаемая
из скрипта, раскрывает около двух байт кучи за одно измерение в окне примерно
64 КБ.
Обычная веб-страница подаёт специально сформированный шрифт через @font-face и вызывает
ctx.measureText(""). Не требуется ни клика, ни разрешения, ни расширения, ни изменения настроек.
OTS работает в том же процессе и не относится к библиотекам, изолированным песочницей, поэтому
раскрываемые байты — это куча контент-процесса, которая может содержать указатели или секреты.
| Файл | Назначение |
|---|---|
make_font.py | Собирает leak.otf и leak.woff — специально сформированные шрифты. |
trigger.html | Минимальная страница, которая загружает шрифт через @font-face и считывает advance для U+FFFF обратно через 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
Затем откройте trigger.html в уязвимой сборке, из пути file:// или
локального статического сервера, который вы контролируете, и нажмите Measure.
.notdef), а advance равен 0. Никакой промежуток не утекает, поскольку выходной поток
обнуляется, а поиск format-4 ограничен своей собственной подтаблицей.make_font.py был выполнен здесь с fontTools
4.63.0 и создал leak.otf (393816 байт) и leak.woff (132372 байта).
Сгенерированный шрифт был разобран обратно из сырых байтов, и каждый сконструированный ингредиент
был подтверждён: maxp.numGlyphs = 65535, hhea.numberOfHMetrics = 65535,
отдельный advance hmtx для каждого glyph id (advance == glyph id), две подтаблицы cmap
(platform 3 encoding 1 format 4 и platform 0 encoding 5 format
14), последний сегмент format-4 endCode = 0xFFFF с нечётным выходящим за границы
idRangeOffset = 0x4001 и nonDefaultUVSOffset = 0x8000 в format-14.trigger.html самостоятельно против локальной уязвимой сборки, чтобы наблюдать утечку.
Этот пакет демонстрирует сконструированный вход и путь обратного чтения; это не
оружифицированный эксплойт и не содержит контролируемого примитива выполнения кода.Это нацелено на локально загруженную сборку с синтетическими, самостоятельно сгенерированными данными шрифта, для описания собственного, уже исправленного CVE автора. Это не атакует какой-либо живой или сторонний сервис.