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

[bugfix] - fix for vulnerability that takes advantage of the hook-only-once logic #1

Closed weizman closed 2 years ago

weizman commented 2 years ago

fixing a vulnerability that existed in the code, POC bypassing Glazier v1.0.4 can be found at the bottom

the vulnerability results due to the hook-only-once logic, which comes to make sure a window is being hooked by Glazier only once, and if for some reason Glazier processes that window the second time, Glazier is told to skip it from being hooked again (as it was already hooked).

implementing such logic is not trivial since it can easily be exploited by attackers.

originally, having an array of windows that were hooked seemed like a good idea, but the POC below proves otherwise.

maybe we should consider adding to that array of windows some indication regarding the origin, so if Glazier processes the same window but with a different origin, it should not ignore it (just a thought)

for now, hook-only-once logic is removed, however it introduces a new logical and performance issue that makes Glazier rehook windows even when not needed - this can easily cause exceptions and unwanted behaviours depend on the callback provided to Glazier

REPRODUCE:

  1. load Glazier in a webpage that is not "https://x.com"
  2. run GLAZE(w=>alert=111)
  3. run POC below

EXPECTED:

alert should fail since now it is 111

ACTUAL:

alert works, Glazier is bypassed

ifr = document.createElement('iframe');
ifr.src = "https://x.com";
document.head.appendChild(ifr);
const zzz = ifr.contentWindow;
setTimeout(() => {
    ifr.src = "about:blank";
    setTimeout(() => {
        zzz.alert.call(top, 111);
    }, 1000);
}, 1000);
weizman commented 2 years ago

thinking about a solution a bit more, i think what should be done is adding the hooked window to the list of already hooked windows only in case that window is same origin and can be hooked. so for example:

  1. cross origin iframe attached to DOM
  2. Glazier skips it and keeps it out of the already hooked list
  3. iframe origin changed to same origin while still attached to DOM
  4. Glazier puts it in the list and hooks it
  5. iframe origin changed to cross origin while still attached to DOM
  6. Glazier takes it out of the list

I think this should maintain the hook-only-once logic while leaving no security holes here - research is needed here