## https://sploitus.com/exploit?id=8AB5E65D-85C5-55D2-B04E-8D38F9802187
# CVE-2026-12295 β srcdoc data on a non-about:srcdoc URI (Firefox UXSS)
Proof of concept: a compromised content process forges a `PDocumentChannel` load
whose `nsDocShellLoadState` carries `SrcdocData` together with an arbitrary
`URI`. Vulnerable builds serve the attacker HTML as the document **at the victim
origin** β same-origin read + exfiltration (UXSS).
Target: Firefox 149.0a1 nightly @ `2fbc0748c4` (vulnerable, pre-fix), macOS arm64.
Fixed in Firefox 152 β bug 2040160, commit `54dc16d08771` "Reject srcdoc data on
non-about:srcdoc URI loads".
## The bug
The parent process deserializes a `nsDocShellLoadState` from contentβparent IPDL
messages. The vulnerable ctor only rejected `javascript:` for content-triggered
loads; `SrcdocData` was not validated. When a load state carries srcdoc data with
a URI that is not `about:srcdoc`, the docshell builds an input-stream channel
that serves the srcdoc HTML **at that URI** β and the resulting document gets the
URI's origin.
The fix adds a `FatalError` in the `nsDocShellLoadState` IPDL ctor when
`!mSrcdocData.IsVoid() && !mURI->SchemeIs("view-source") && !NS_IsAboutSrcdoc(mURI)`,
plus hardening asserts in `nsDocShell` and `Document::StartDocumentLoad`.
Sibling bug: CVE-2026-74939 (`RemoteTypeOverride`) β same ctor, same carrier
message, same delivery machinery (see `../poc-cve-2026-74939`). This PoC reuses
that forge with a different field flipped; no `RemoteTypeOverride` needed.
## The demo
- Attacker page `srcdoc.html` at `http://127.0.0.1:8778` (origin A, compromised
via the stage-1 wasm primitives in `wasm-bytes.js`).
- It forges a `PNecko::PDocumentChannel` ctor: `URI = http://localhost:8778/nav.html`
(origin B), `SrcdocData = `, top-level `BrowsingContext` of a
same-process `about:blank` popup β delivered through the real mojo send path
(`operator new` β `IPC::Message` ctor β `Pickle::WriteBytes` β
`MessageChannel::Send`).
- The parent accepts the load, process-switches the popup to
`webIsolated=http://localhost`, and the docshell serves **our** HTML as the
document at origin B.
- Payload fetches `/secret.txt` (same-origin on B β a real cross-origin fetch
from A would be CORS-blocked) and exfiltrates it to A.
Evidence (`/tmp/srv.log` after `./irun`):
```
REQ_GET /secret.txt
REQ_GET /exfil?d=flag%7Bsrcdoc-crossed-origins-2040160%7D
```
## Files
| File | Purpose |
| ---- | ------- |
| `srcdoc.html` | PoC page, run inside the compromised content process |
| `forge.py` | builds the forged message (self-verifying) β `forge.bin`/`forge.json` |
| `wasm-bytes.js` | stage-1 primitives: arb R/W, funcref calls (CVE-2026-2796) |
| `mdrive2.py` | marionette harness to launch and drive Nightly |
| `irun` | instrumented run: lldb attach to parent + MOZ_LOG/srv.log evidence |
| `profile.user.js` | Firefox profile prefs (fission on, dump enabled) |
| `parse_dc.py` | byte-exact parser for captured DocumentChannel messages |
| `captured-messages/dc_1.bin` | real captured message, used as the forge template |
| `nav.html`, `secret.txt` | victim-page fixture + secret for the demo |
## Run
Prerequisites: vulnerable Nightly build at `/Users/sid/gecko-2766/obj-browser`,
`.venv` with psutil, and a web server on `0.0.0.0:8778` serving this directory
(e.g. `python3 /tmp/srv.py`, logging to `/tmp/srv.log`).
```
python3 forge.py # build forge.bin/forge.json (self-verifying)
./irun # launch + inject + collect evidence
```
Note: a content process may SIGSEGV on teardown after the send (GC over the fake
objects); all evidence is emitted before that.
## References
- Mozilla bug 2040160 β fix commit `54dc16d08771` (MFSA 2026-57, Firefox 152)
- CVE-2026-74939 β sibling load-state forge (`RemoteTypeOverride`)
- CVE-2026-2796 β stage-1 content RCE providing the primitives