Анализ CVE-2025-6554: движок возвращал “невидимое” значение вместо ошибки — и открыл память

История одной "дыры", ставшей эксплойтом.


w1rf91v2ftlh4rnl3ildt70axdyhye8a.jpg


В июне 2025 года в JavaScript-движке V8 была обнаружена критическая уязвимость, получившая идентификатор CVE-2025-6554 . Она связана с особенностями обработки переменных, находящихся в состоянии "временной мёртвой зоны" (TDZ), и уже активно использовалась злоумышленниками до выхода исправления от Google.

Исследователь с ником @DarkNavyOrg опубликовал пример , демонстрирующий суть проблемы. В центре уязвимости — функция, в которой переменная y используется до объявления. В JavaScript это должно приводить к ошибке ReferenceError, поскольку переменные, объявленные через let и const, недоступны до момента инициализации. Однако в V8 подобные случаи обрабатываются особым образом — вместо ошибки движок возвращает скрытое значение под названием "hole", которое служит маркером незаполненного места в массиве или недоступной переменной.

В демонстрационном коде уязвимость возникает при удалении свойства с помощью конструкции delete x?.[y]?.a, где переменная y ещё не определена. В нормальной ситуации обращение к ней должно завершиться ошибкой. Однако из-за недоработки в логике байткода, когда x имеет значение undefined, движок пропускает проверку y на наличие значения hole и просто возвращает его, позволяя использовать это значение в дальнейшем.

В прошлом аналогичная уязвимость уже возникала в 2023 году — тогда CVE-2023-3079 также использовала "hole" для обхода защиты и выполнения произвольного кода. В текущем случае проблема повторилась, но в другой части движка. В ходе анализа выяснилось, что проблема кроется в генерации байткода. В момент выполнения delete-операции проверка на наличие "hole" происходит корректно, но затем результат сохраняется и воспринимается как проверенный, даже при последующих обращениях. Это приводит к тому, что при возврате значения y из функции соответствующая проверка уже не выполняется, и движок возвращает необработанный "hole".

Причина кроется в механизме управления так называемыми "bitmap'ами" проверки hole-состояний. Эти структуры фиксируют, какие переменные уже были проверены на наличие "дыры", чтобы не дублировать проверки и не снижать производительность. Однако если проверка выполняется вне нужной области видимости, она может ошибочно считаться глобальной. В результате при следующем обращении к переменной проверка пропускается, и движок возвращает значение, которое по идее не должно было попасть в руки пользователя.

Патч, выпущенный Google, корректирует этот механизм. Теперь каждый участок байткода имеет изолированную область для контроля hole-проверок, и возврат значения переменной снова сопровождается корректной валидацией. Это исключает возможность утечки "дыры" в обход стандартных защитных механизмов.

Сама уязвимость выглядит как нечто технически сложное и специфическое, но последствия её эксплуатации могут быть весьма серьёзными. Получив доступ к скрытым внутренним значениям V8, атакующий может сконструировать примитивы для чтения и записи произвольной памяти, что в итоге приводит к выполнению произвольного кода в контексте браузера. Это особенно опасно в случае атак через JavaScript в браузерах Chrome и других продуктах на базе Chromium.

Инцидент показывает, насколько важно точно и строго соблюдать логику обработки ошибок и промежуточных состояний даже на уровне генерации байткода. В данном случае одна незащищённая переменная оказалась точкой входа для полноценной уязвимости нулевого дня , уже используемой в реальных атаках.