Open matanber opened 5 months ago
Hi @matanber,
As you've probably seen, there are quite a few reported issues against the Snow project, which we haven't addressed yet on purpose.
These issues have taught us that browsers are just too unable to help us address this issue in the user land (meaning by implementing JS rules at runtime).
By "this issue" I refer to the ability to form new same origin realms and access them even when an app wants to forbid it.
This is also known as the same origin concern which we write about a lot and are trying to convince browser vendors to help defend against this problem natively (see our explainer).
I'm telling you this because I don't want you to spend some of your precious time on Snow until we either address these issues or decide to form a different version of a solution to this problem (WIP).
I take responsibility for not making this clear in the README file, I will make sure to do so.
I at least hope this was fun for you and helped you comprehend this new type of problem we're trying to defend and address - with hope to succeed one day!
Either way, thanks for the effort, it is much appreciated.
(p.s. have a go at LavaDome - I think is has a better shot at succeeding than Snow, at least for now)
And as for this bypass specifically - awesome work. Truly.
function swap2(aURL) {
const url = typeof aURL === 'string' ? aURL : toString(aURL);
if (stringStartsWith(url, 'blob')) {
if (!blobs.has(url)) {
const content = syncGet(url);
const preloadURL = createObjectURL(new Blob([`window.parent.SNOW_WINDOW(window);`], {
type: 'text/javascript'
}));
const prefix = `<script src=${preloadURL}></script>`;
const js = prefix + '\n\n' + content;
blobs.set(url, createObjectURL(new Blob([js], {
type: 'text/html'
})));
}
return blobs.get(url);
}
return url;
}
Object.defineProperty(win.HTMLIFrameElement.prototype, 'src', {
configurable: true,
enumerable: true,
set: function(url) {
return setSrc(this, "IFRAME", swap2(url));
}
});
Object.defineProperty(win.HTMLFrameElement.prototype, 'src', {
configurable: true,
enumerable: true,
set: function(url) {
return setSrc(this, "FRAME", swap2(url));
}
});
function setSrc(element, tag, url) {
switch (tag) {
case 'IFRAME':
return bag.iframeSrc.call(element, url);
case 'FRAME':
return bag.frameSrc.call(element, url);
default:
return null;
}
}
The code above is a solution to the problem,, but I only tested the code in the issue
. However, I have a few questions. First, why didn't you use MutationObserver
? Second, why did you use the iframe onload
event? When I examined the code, I couldn't see any benefits of using 'onload'.
MutationObserver
isn't a synchronous API, so by the time it tells you a new iframe is introduced to the page, it's too late because the attacker probably gained access to it before you.
The onload
event on the other hand acts synchronously against iframes loading local resources (e.g. about:blank
or blob:
), so it answers that need that the MutationObserver
approach doesn't.
Hope this helps.
I got confused because MutationObserver
was used in that project, which caused me to be misled. Your explanation helped me understand, thank you.
It's all a matter of what you're trying to defend 😉
It is possible to provide some protections against this bypass without Realm Initialization Control, although RIC would make it much easier to solve.
In order to prevent Blob URIs from being created within workers, snow overrides the
Worker
constructor using the following code:This code overrides the
URL.createObjectURL
function in every worker that is created from a URL that begins with 'blob'. Because URI schemes are case-insensitive, this can be bypassed simply by creating a worker with a URL that begins with 'Blob'. Here is a little demo for that:Building on top of arxenix's brilliant bypass in issue #43, this can be used to bypass Snow using the following PoC: