Логотип
Главная | Статьи | Эксплуатация JIT-компиляторов: pwn через V8/SpiderMonkey
Эксплуатация JIT-компиляторов: pwn через V8/SpiderMonkey

Эксплуатация JIT-компиляторов: pwn через V8/SpiderMonkey

26 февраля, 2026

29

JIT (Just-In-Time) компиляторы – одна из самых лакомых целей в браузерном пентесте. По данным Microsoft, около 45% CVE в V8 связаны именно с JIT-движком. Сложность архитектуры + спекулятивные оптимизации = богатая поверхность атаки.​

Как работает JIT (и где он ломается)

JavaScript-движок сначала интерпретирует код через базовый интерпретатор (в V8 — Ignition), накапливая статистику вызовов. Как только функция становится «горячей» (вызывается часто), JIT берёт управление и компилирует её в нативный машинный код с применением спекулятивных оптимизаций. В V8 цепочка компиляции выглядит так:

TurboFan — старший тир, самый агрессивный оптимизатор и самый уязвимый. В SpiderMonkey (Firefox) аналогичную роль играет IonMonkey.

Суть атаки: JIT доверяет своим предположениям о типах. Если заставить его удалить guard-проверку, а потом нарушить предположение — получаем type confusion с memory corruption.

Ключевые классы уязвимостей

1. Type Confusion

Самый распространённый класс. TurboFan строит Map (аналог «типа» объекта) и генерирует код, обращающийся к полям объекта по фиксированным офсетам. Если Map меняется незаметно для компилятора – код читает/пишет не туда.​

CVE-2022-1364 (эксплуатировался in-the-wild): некорректное отслеживание типов в пайплайне TurboFan позволяло выполнить запись в произвольную память.

CVE-2023-3420: ошибка в моделировании побочных эффектов узла StackCheck – он был помечен kNoWrite, хотя мог вызывать HandleInterrupts, меняющий Map объекта. Итог: OOB-write через несинхронизированные офсеты PropertyArray vs NamedDictionary.

2. Bounds Check Elimination (BCE)

JIT пытается доказать, что индекс массива всегда в пределах bounds, и убирает соответствующий guard. Если доказательство неверно – получаем Out-of-Bounds read/write.​

Классическая техника: через integer overflow заставить TurboFan думать, что index < array.length всегда true. Результат – прямой OOB-доступ в heap.

3. Redundancy Elimination + Side Effect Modeling

TurboFan удаляет «дублирующие» проверки, если уверен, что между ними ничего не изменилось. Баги возникают, когда движок неправильно моделирует побочные эффекты. Именно так работал CVE-2023-3420: StackCheck считался “нейтральным”, но на деле мог менять объекты через interrupt handling.

SpiderMonkey: JIT Spraying

В Firefox IonMonkey помечает JIT-страницы как r-x (нет записи), что усложняет прямой шеллкод-инжект. Обходится через JIT Spraying – кодирование шеллкода в константных значениях (float64), которые JIT вставляет inline.​

После получения произвольного read/write в V8 heap та же техника актуальна для обхода heap sandbox.

Построение примитивов: addrOf и fakeObj

Это фундаментальная пара примитивов для любого браузерного эксплойта:

  • addrOf(obj) – возвращает адрес произвольного JS-объекта в V8 heap
  • fakeObj(addr) – заставляет движок считать, что по адресу addr лежит JS-объект

Обычно получаются через type confusion с double-массивом: читаем float64 из Uint64Array-alias поверх объектного слота – это и есть адрес.

Цепочка эксплойта: от JS до RCE

Полная цепочка для renderer sandbox выглядит так:

  1. Триггер JIT-бага → type confusion или OOB
  2. OOB write в NamedDictionary → контроль над capacity
  3. addrOf + fakeObj через поддельный NamedDictionary-entry
  4. Arbitrary read/write в V8 heap (через corrupted elements pointer у float64 array)
  5. Обход heap sandbox: JIT Spraying – шеллкод в float-константах → патч jit_code_addr
  6. Sandbox escape – нужен второй баг (Chrome sandbox или kernel) для полного компромисса

Свежие CVE (2024–2025)

CVEДвижокТипВектор
CVE-2024-9602V8 (Chrome)Type ConfusionПосещение вредоносной страницы
CVE-2024-7971V8 (Chrome)Type ConfusionRemote, активно эксплуатировался
ZDI-24-664SpiderMonkeyOOB WritePwn2Own 2024
CVE-2024-29943 SpiderMonkeyOOB Read/WriteRemote RCE
CVE-2025-10585V8 (Chrome)0-dayАктивная эксплуатация in-the-wild

Инструментарий для ресёрча

  • Turbolizer – визуализация Sea of Nodes графа TurboFan, показывает все фазы оптимизации​
  • rr (Mozilla) – time-travel отладчик, незаменим для воспроизводимой отладки эксплойтов
  • GDB + GEF – для инспекции V8 heap напрямую
  • %DebugPrint(obj) – дамп внутреннего состояния JS-объекта включая Map, elements, code pointer

Защиты и их обходы

ЗащитаСутьОбход
V8 Heap SandboxИзолирует V8 heap, блокирует перезапись WASM RWX​JIT Spraying через float-константы
SpiderMonkey r-x JIT pagesНет записи в JIT-памятьJIT Spraying через константы
Speculation Guards / CheckMapsRuntime-верификация типовПоиск неучтённых side effects (CVE-2023-3420)
Pointer CompressionСжатие указателей в 32 битаУтечка cage base через объекты

JIT-компиляторы – это вечный компромисс между скоростью и безопасностью. Пока браузерные вендоры не уберут JIT (а они не уберут – слишком дорого по производительности), эта поверхность атаки никуда не денется.