WASM and WASI Sandbox Escape Notes for Bug Hunters
Most "WASM escape" tweets are Chrome bugs. You will not casually leave the Wasm sandbox from a random bounty SPA. What you will find are broken host bridges: JS imports that trust Wasm too much, or WASI apps that hand out filesystem capabilities like candy.
Separate the layers before you write a title.
Browser Wasm: hunt the glue
In page context, Wasm runs with the same origin powers as JS—network via imported functions, DOM if you pass handles, nothing magical. Bugs look like:
- Linear memory parsed as HTML or script without sanitization
- Host functions that take Wasm-controlled pointers and call
eval, dynamicFunction, or native plugins - Shared memory + Atomics races in multi-threaded Wasm where the host assumes immutability
- Length fields in Wasm memory that JS trusts when slicing ArrayBuffers into strings
Start from WebAssembly.instantiate sites in the bundle. List imports. Any import that writes files, opens sockets, or touches Electron IPC is your real attack surface.
I treat Wasm memory as an attacker-controlled byte buffer that JS will eventually TextDecoder into the DOM. That path is XSS, not a VM escape—and XSS still pays. Same for exporting functions that the page calls with privileged tokens sitting in closures.
WASI and desktop embeddings
WASI capability models fail when the embedder pre-opens broad directories or forwards unchecked paths. Look for:
path_openwith user-controlled paths past the preopened root- Environment variables that point the runtime at host secrets
- Custom host ABI functions documented as "temporary" that never got locked down
- Clock or random imports that are fine, versus sock_* imports that suddenly become SSRF from a plugin
Proof should be a file read outside the intended directory or a connection to an internal IP the policy cares about—not a crash. On Tauri/Electron, say which process loaded the module—renderer vs main changes the blast radius.
Severity honesty
A browser crash in Wasm is often out of scope. A host import that reads ~/.ssh from a desktop Wasm plugin is a different conversation. Match the title to the boundary you crossed: JS trust boundary, OS capability, or actual engine bug.
If you only showed a panic in a sandbox that auto-restarts, call it availability and move on unless the program pays for that class.
Do not title the report "sandbox escape" unless you crossed into host OS or a higher-privilege process. Overclaiming burns trust with triage teams that have seen too many renamed XSS tickets.
Lab habits
Keep repro modules tiny. Prefer deterministic memory layouts. Record engine versions. For Electron or Tauri apps embedding Wasm, say so in the first paragraph so triage does not shelve it as "browser issue."
Ship a minimal .wat or prebuilt .wasm with the report when the bug is in host glue. Triage should not need your entire product build to see the import call that reads the canary file.
Hunt imports and capabilities. The sandbox marketing slides are not the product.