
Цепочка эксплойтов PoC для CVE-2026-2796: побег из песочницы SpiderMonkey WebAssembly (путаница типов сигнатур -> произвольное чтение/запись -> удалённое выполнение кода)
Цепочка эксплойта, подтверждающая концепцию, для CVE-2026-2796 — ошибка JIT-компиляции / путаница типов (type confusion) в оптимизации импорта WebAssembly в движке SpiderMonkey от Mozilla (компонент «JavaScript: WebAssembly»). Специально сформированный wasm-модуль получает произвольное чтение/запись во всём процессе хоста и произвольное выполнение нативного кода, совершая побег из песочницы WebAssembly.
e2acef67 («Bug 2013165 - Fix import optimization»)Когда wasm-модуль импортирует JS-функцию, SpiderMonkey применяет оптимизацию в MaybeOptimizeFunctionCallBind (js/src/wasm/WasmInstance.cpp), которая разворачивает импорты вида Function.prototype.call.bind(fn) и сохраняет напрямую как вызываемый объект импорта. При этом не проверялось, является ли связанное значение само по себе :
fn+ if (boundThis.toObject().is<JSFunction>() &&
+ boundThis.toObject().as<JSFunction>().isWasm()) {
+ return nullptr;
+ }
Из-за отсутствия проверки импорт трактуется как функция исходно-wasm: JS-обёртка, а вместе с ней и проверка сигнатуры, пропускается. Таким образом, былая функция может быть вызвана через объявленный тип импорта, не соответствующий её реальному типу. Значения передаются без изменений в регистрах; меняется только их интерпретация (например, управляемое атакующим i64 используется как GC-указатель (ref $t), и наоборот).
| Файл | Этап | Результат |
|---|---|---|
poc-crash.js | Путаница сигнатур | i64.const 0xDEADBEEF разыменовывается как указатель funcref → SIGSEGV по адресу 0xdeadbf2f |
poc-addrof.js | addrOf + fakeobj | Путаница в обоих направлениях (i64 ↔ (ref $t)) → поддельный WasmArrayObject (numElements_ @+16, data_ @+24, инлайн-элементы @+40) → произвольное чтение/запись в любом месте процесса |
poc-recon.js | Разведка раскладки | Нативный указатель JSFunction @+0x20 → утечка базового адреса бинарника; указатель typeDef WasmFuncRef @+0x40 |
poc-forge.js | Управление потоком | Поддельный funcref проходит проверку типа call_ref; цель вызова загружается из [funcref+0x38] |
poc-rce.js | Выполнение кода | Поддельный funcref → system("touch /tmp/CVE-2026-2796-PWNED") через утёкший system() (база бинарника + запись в GOT @ base+0x11d47b0) |
Примитив путаницы получается в точности как в собственном регрессионном тесте Mozilla (js/src/jit-test/tests/wasm/regress/bug2013165.js): импорт Function.prototype.call.bind(wasmExport) во второй модуль, объявление импорта которого несёт другую сигнатуру, затем ref.func + call_ref.
# Исходники Firefox @ 2fbc0748c460b38fc95407a3f14c41d12fb12026 (2026-01-14,
# ночной билд Firefox 148 — до исправления). Подойдёт любая ревизия до 148.
cd js/src
../../configure --enable-debug --enable-optimize --without-intl-api \
--enable-project=js # objdir, напр. js/src/_obj
cd _obj && make -j8
# бинарник: dist/bin/js (сообщает "JavaScript-C149.0a1")
JS=/path/to/dist/bin/js
$JS poc/poc-crash.js # SIGSEGV по 0xdeadbf2f
$JS poc/poc-addrof.js # выводит [+] arbitrary read OK / write OK
$JS poc/poc-recon.js # дамп памяти JSFunction / WasmFuncRef
$JS poc/poc-forge.js # падение с PC = внедрённая canary
rm -f /tmp/CVE-2026-2796-PWNED
$JS poc/poc-rce.js # создаёт /tmp/CVE-2026-2796-PWNED через system()
На пропатченной сборке (Firefox ≥ 148) poc-crash.js вместо этого выбрасывает
TypeError: bad type — проверка сигнатуры восстановлена.
Смещения приведены для macOS arm64 (оболочка js, именно эта ревизия и флаги сборки):
WasmArrayObject { +16 numElements, +24 data_, +40 инлайн-данные },
нативный JSFunction @ +0x20, WasmFuncRef { +0x40 typeDef, +0x38 цель
вызова }. Они эмпирически проверяются во время выполнения самотестами PoC;
для других сборок/архитектур требуется повторный вывод (recon-PoC автоматизирует большую его часть). На arm64-бинарниках (не arm64e) PAC отсутствует; JIT-области не доступны для записи в момент вызова, поэтому цепочка перехватывает существующую цель вызова вместо внедрения кода.
Анализ второго этапа см. в docs/full-escape.md: побег из песочницы ОС из выполнения кода в renderer-процессе через CVE-2026-2768 (Bug 2014101, внеграничная запись в IndexedDB родительского процесса) — «второй баг» полной компрометации Firefox. Используемое здесь уязвимое дерево исходников также предшествует и этому исправлению.
Данный эксплойт совершает побег из песочницы wasm-движка (изоляция линейной памяти / GC) и обеспечивает выполнение нативного кода в текущем процессе. В реальной браузерной атаке это приводит к выполнению кода внутри content-процесса Firefox; побег из песочницы ОС («второй баг»: путаница IPC или эксплойт ядра) — отдельная задача и не является частью данного PoC.
javascript.options.wasm=false блокирует вектор срабатывания.Только для исследований в области безопасности, обучения и защитного тестирования. Уязвимость исправлена в текущих выпусках Firefox/Thunderbird. Не используйте против систем, которыми вы не владеете или на тестирование которых у вас нет явного разрешения.