- Create a sandboxed domain, where user can execute script but has no harm in the main product (e.g. *.googleusercontent.com)
- Render the content using sandboxed iframe, with Data URL or srcdoc (i.e. the script will have opaque origin).
With Spectre, the latter option becomes vulnerable 😕
Site Isolation
At the time of writing, Site Isolation is fully enabled only on desktop platforms of Chromium . So this post will only focus on the desktop platforms.
While Site Isolation takes care of isolating cross-site documents (i.e. option #1), it doesn't take care of same-site sandboxed iframes at the time of writing. Data URL and srcdoc (i.e. about:srcdoc) are considered same-site to the navigation initiator.
Therefore, script executing inside Data URL or srcdoc sandboxed iframes can read data of parent frame (i.e. the navigation initiator).
A real world example
Google Earth has a feature to import KML map. Google Earth renders KML map in a sandboxed iframe with srcdoc. And because KML map can have arbitrary contents including scripts, this allows an attacker with Spectre exploit to read data of earth.google.com.
This bug was reported to Google but marked as Won't Fix 😂
The following PoC shows the ability for an attacker to execute script inside the sandboxed iframe by showing a random YouTube video 😁
(* Audio is loud so turn off if you don't want to listen 😊)
A Hack to render untrusted content in an isolated process
As you might have guessed from the title of this blog post, there is a solution to this problem 😉
I've mentioned in my Site Isolation blog post, that a Blob URL with opaque origin will have its process isolated based on origin AND path.
We can use this fact to create a Blob URL from an opaque origin document. Which will result in creating a Blob URL with opaque origin, which is guaranteed to have an isolated process per new Blob URL if navigated.
The following PoCs shows this in action 😎
Conclusion
If your site renders untrusted scripts using sandboxed iframe with either srcdoc or a Data URL, you should use a Blob URL with opaque origin instead 🙂