Как опечатка в один символ в Firefox привела к удалённому выполнению кода
Автор: Евгений Падежнов
Разработка программного обеспечения требует точности. Неверно расположенный символ может привести к сбою приложений, повреждению данных или созданию уязвимостей безопасности. В январе 2026 года разработчики Firefox усвоили этот урок, когда опечатка в один символ — использование & вместо | — открыла путь к удалённому выполнению кода в миллионах браузеров.
Опечатка на миллион долларов: & против |
Согласно cybersecuritynews.com, уязвимость возникла в движке JavaScript SpiderMonkey браузера Firefox. Разработчик, работавший над сборкой мусора WebAssembly, набрал & (побитовое И) вместо | (побитовое ИЛИ) в файле js/src/wasm/WasmGcObject.cpp. Ошибочная строка выглядела так:
oolHeaderOld->word = uintptr_t(oolHeaderNew) & 1;
Правильная версия должна была быть:
oolHeaderOld->word = uintptr_t(oolHeaderNew) | 1;
Эта разница в один символ полностью изменила результат операции. Побитовое ИЛИ с 1 устанавливает младший значащий бит в 1. Побитовое И с 1 изолирует только этот бит, давая 0 для выровненных указателей. Результат: Firefox сохранял нулевые значения вместо правильно помеченных пересылочных указателей.
Как отмечено на meterpreter.org, уязвимость была внесена через коммит fcc2f20e35ec 19 января 2026 года. Опечатка прошла через первоначальные проверки кода, потому что побитовые операции синтаксически корректны, даже когда логически неверны. Современные компиляторы не отлавливают этот тип семантических ошибок.
Ошибка затронула функцию WasmArrayObject::obj_moved(), которая обрабатывает память при перемещении массивов WebAssembly во время сборки мусора. Эта функция должна гарантировать, что младший значащий бит (LSB) пересылочных указателей установлен в 1. Опечатка нарушила этот критический инвариант.
Сборка мусора WebAssembly: где всё пошло не так
Понимание уязвимости требует изучения реализации WebAssembly в Firefox. WebAssembly (Wasm) позволяет запускать скомпилированный код в браузерах со скоростью, близкой к нативной. Движок SpiderMonkey Firefox управляет памятью Wasm через сборку мусора, автоматически освобождая неиспользуемую память.
Уязвимость конкретно затронула внешние (out-of-line, OOL) массивы WebAssembly. SpiderMonkey хранит маленькие массивы внутри объектов. Большие массивы используют отдельные выделения памяти — внешнее хранилище. Движок использует пометку указателей для различения этих типов хранения.
Согласно анализу безопасности, функция isDataInline() проверяет, является ли (headerWord & 1) == 0 для определения внутреннего хранилища. Установка LSB в 1 помечает массивы как внешние. Опечатка привела к тому, что система сохраняла ноль вместо помеченного указателя, заставляя OOL массивы выглядеть как внутренние.
Это несоответствие создало расхождение интерпретаций между сборщиком мусора и JIT-компилятором. Сборщик мусора обрабатывал перемещённые массивы как внутренние, в то время как JIT-компилятор ожидал внешние данные. Эта путаница типов позволила повредить память.
Ограничение области действия оказалось критическим: уязвимость затрагивала только функции WebAssembly, оптимизированные Ion, продвинутым JIT-компилятором Firefox. Базовый компилятор остался незатронутым, потому что он не использует тот же механизм обновления стекового фрейма.
От опечатки до полной компрометации системы
Исследователь безопасности Erge обнаружил уязвимость при изучении исходного кода Firefox 149 Nightly в поисках вдохновения для CTF-задач. Процесс обнаружения подчёркивает, как ручная проверка кода выявляет тонкие ошибки, которые пропускают автоматизированные инструменты.
Согласно threatlabsnews.xcitium.com, уязвимость путаницы типов предоставляла атакующим "примитивный доступ чтения/записи" к памяти. Эксплуатация требовала нескольких шагов:
- Вызвать путаницу типов через специально созданный код WebAssembly
- Использовать несоответствие для чтения/записи произвольных адресов памяти
- Построить надёжные примитивы эксплуатации
- Достичь выполнения кода в процессе рендерера
Влияние уязвимости выходило за рамки простых сбоев. Успешная эксплуатация позволяла:
- Кражу сессий через доступ к cookies
- Компрометацию учётных данных, включая токены SSO
- Утечку данных из других вкладок браузера
- Выход из песочницы, ведущий к установке вредоносного ПО
- Полный захват браузера
Ключевой момент: Уязвимость не требовала взаимодействия с пользователем, кроме посещения вредоносной веб-страницы. Атакующие могли встраивать эксплойт-код в рекламу, скомпрометированные веб-сайты или фишинговые страницы.
Контекст рефакторинга имеет значение. Опечатка произошла во время рефакторинга метаданных массивов сборки мусора WebAssembly. Рефакторинг часто вносит ошибки, когда разработчики модифицируют работающий код. Проверки кода во время рефакторинга фокусируются на архитектурных изменениях, потенциально упуская тонкие ошибки реализации.
Уроки для практик безопасной разработки
Опечатка в Firefox предлагает несколько уроков для команд разработки. Инструменты статического анализа не смогли поймать эту ошибку, потому что код оставался синтаксически корректным. Побитовые операции компилируются правильно независимо от логического намерения.
Проверенный подход: Реализуйте несколько уровней обнаружения ошибок:
Юнит-тесты для критических операций: Явно тестируйте механику пересылочных указателей. Проверяйте, что манипуляции с битами дают ожидаемые результаты. Проверяйте граничные случаи, такие как нулевые значения.
Фаззинг реализаций WebAssembly: Запускайте непрерывный фаззинг на путях кода Wasm. Конкретно нацеливайтесь на сценарии сборки мусора. Мониторьте уязвимости путаницы типов.
Чек-листы проверки кода: Помечайте все побитовые операции для дополнительной проверки. Требуйте явных комментариев, объясняющих логику манипуляций с битами. Сравнивайте со спецификациями проектирования.
Утверждения времени выполнения: Добавляйте проверки инвариантов в режиме отладки. Проверяйте согласованность пометки указателей. Обнаруживайте несоответствующие интерпретации типов на ранней стадии.
Распространённая ошибка: Предполагать, что проверки синтаксиса отлавливают все ошибки. Семантические ошибки требуют других стратегий обнаружения. Уязвимость Firefox прошла через первоначальные проверки именно потому, что выглядела правильно.
Команды разработки должны относиться к коду управления памятью как к критичному для безопасности. Реализации WebAssembly заслуживают особого внимания, учитывая их сложность и поверхность атаки. Один символ в логике сборки мусора скомпрометировал миллионы браузеров.
На практике организациям нужны систематические подходы для предотвращения подобных уязвимостей. Ответ Mozilla включал исправление конкретной ошибки и проверку связанных путей кода. Инцидент вызвал более широкие дискуссии о моделях безопасности WebAssembly.
Техническое погружение: понимание пометки указателей
Пометка указателей оптимизирует использование памяти и хранение информации о типах. Системы кодируют метаданные в неиспользуемых битах указателей. На 64-битных системах указатели редко используют все доступные биты. Младшие значащие биты часто остаются доступными для пометки.
Реализация Firefox использует LSB для различения типов хранения массивов. Установка этого бита в 1 указывает на внешнее хранилище. Опечатка нарушила эту схему пометки, вызвав каскадные сбои.
Механика уязвимости раскрывает тонкие взаимодействия между компонентами:
Ожидания сборщика мусора: Код GC ожидает пересылочные указатели с LSB, установленным в 1. Опечатка вместо этого давала нулевые значения.
Предположения JIT-компилятора: Оптимизированный код Ion предполагает правильную пометку указателей. Несоответствующие предположения привели к повреждению памяти.
Расхождение поведения во время выполнения: Разные компоненты движка интерпретировали расположение памяти по-разному. Это расхождение позволило эксплуатацию.
Проверенный подход: Проектируйте схемы пометки указателей с явной валидацией. Включайте проверки времени выполнения в отладочных сборках. Чётко документируйте инварианты пометки. Тщательно тестируйте манипуляции с метками.
Инцидент также подчёркивает риски рефакторинга. Модификация низкоуровневого кода управления памятью требует крайней осторожности. Даже опытные разработчики делают ошибки при обновлении сложных систем.
Влияние на индустрию и ответные меры
Уязвимость Firefox демонстрирует сложность современных браузеров. Миллионы строк кода взаимодействуют через сложные интерфейсы. Ошибка в один символ в одной подсистеме компрометирует всё приложение.
Производители браузеров ответили усилением контроля безопасности WebAssembly. Команды Chrome и Safari проверили свои реализации на наличие похожих проблем. Инцидент вызвал общеотраслевые дискуссии о безопасности памяти.
Ключевой момент: Мощь WebAssembly приходит с обязанностями по безопасности. Почти нативная производительность требует тщательной реализации. Сборка мусора добавляет ещё один уровень сложности.
Исследователи безопасности получили ценные знания из этой уязвимости. Класс ошибки — путаница типов через неправильную пометку указателей — применим за пределами Firefox. Похожие проблемы могут затронуть другое ПО, использующее помеченные указатели.
Если это работает — это правильно. Но работающий код не обязательно безопасный код. Уязвимость Firefox функционировала правильно во многих сценариях. Только специфические паттерны WebAssembly вызывали эксплуатируемое условие.
Организации должны реализовывать стратегии глубокой защиты. Изоляция браузера ограничила влияние уязвимости. Изоляция процессов предотвратила полную компрометацию системы. Эти архитектурные решения важны, когда ошибки неизбежно происходят.
Часто задаваемые вопросы
Что именно за опечатка в Firefox вызвала уязвимость?
Разработчик набрал & (побитовое И) вместо | (побитовое ИЛИ) в коде сборки мусора WebAssembly. Опечатка произошла в файле js/src/wasm/WasmGcObject.cpp 19 января 2026 года. Это изменение в один символ заставило Firefox сохранять нулевые значения вместо правильно помеченных пересылочных указателей, создавая эксплуатируемую уязвимость путаницы типов.
Могли ли автоматизированные инструменты поймать эту уязвимость?
Стандартный статический анализ и компиляторы не смогли поймать эту ошибку, потому что код оставался синтаксически корректным. Операции побитового И компилируются правильно, даже когда логически неверны. Обнаружение требует семантического анализа, комплексного тестирования логики пометки памяти или ручной проверки кода экспертами по безопасности, знакомыми с кодовой базой.
Насколько серьёзным было влияние этой опечатки на безопасность?
Уязвимость позволяла полное удалённое выполнение кода в Firefox. Атакующие могли красть сессионные cookies, получать доступ к учётным данным, включая токены SSO, читать данные из других вкладок браузера и потенциально выходить из песочницы браузера для установки вредоносного ПО. Никакого взаимодействия с пользователем не требовалось, кроме посещения вредоносной веб-страницы.
Затронуты ли другие браузеры похожими уязвимостями?
Эта конкретная уязвимость затронула только движок SpiderMonkey Firefox. Однако класс ошибки — путаница типов через ошибки пометки указателей — теоретически может затронуть любое ПО, использующее похожие техники. Chrome, Safari и другие браузеры используют разные реализации WebAssembly, но сталкиваются с похожими проблемами сложности.
Заключение
Один символ — один побитовый оператор — создал критическую уязвимость безопасности, затронувшую миллионы пользователей Firefox. Инцидент подчёркивает фундаментальные истины о безопасности программного обеспечения. Сложные системы терпят неудачу неожиданными способами. Ручная проверка кода выявляет ошибки, которые пропускают автоматизированные инструменты. Стратегии глубокой защиты ограничивают ущерб при возникновении уязвимостей. Команды разработки должны относиться к каждому символу в критичном для безопасности коде как к потенциально опасному. Опечатка в Firefox служит постоянным напоминанием о том, что в разработке программного обеспечения самые маленькие детали несут наибольшие риски. ```