Closed Bug 586878 (ZDI-CAN-883) Opened 15 years ago Closed 14 years ago

nsFrameManager Remote Code Execution Vulnerability (ZDI-CAN-883)

Categories

(Core :: CSS Parsing and Computation, defect)

defect
Not set
critical

Tracking

()

RESOLVED FIXED
mozilla2.0
Tracking Status
status2.0 --- unaffected
blocking1.9.2 --- -
status1.9.2 --- wontfix
status1.9.1 --- ?

People

(Reporter: reed, Unassigned)

References

(Blocks 1 open bug)

Details

(Keywords: crash, regression, testcase, Whiteboard: [sg:dos] frame-poisoned crash, fixed on trunk by 508473)

Attachments

(2 files)

Attached file PoC
ZDI-CAN-883: Mozilla Firefox nsFrameManager Remote Code Execution Vulnerability -- CVSS ---------------------------------------------------------------- 10, (AV:N/AC:L/Au:N/C:C/I:C/A:C) -- ABSTRACT ------------------------------------------------------------ TippingPoint has identified a vulnerability affecting the following products: Mozilla Firefox 3.6.x -- VULNERABILITY DETAILS ----------------------------------------------- This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations of Mozilla Firefox. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file. The specific flaw exists within the way the application recursively destroys frames. When a frame contains a child element with a particular style during destruction, the application will add 2 references of the overflown element to a list. On destruction of the frame, the application will then iterate through this list, and then free the reference twice. This can lead to code execution under the context of the application. The issue is located within the implementation of nsCSSFrameConstructor.cpp. When calling DoDeletingFrameSubtree, the application will add a reference to the div with the large attribute to the destroyQueue. When the application destroys the current frame, the following function will get called. This will determine which frames will be destroyed by calling a recursive function and then adding each subelement to a list called destroyQueue. This will then call DoDeletingFrameSubtree. layout/base/nsCSSFrameConstructor.cpp:7129 static nsresult DeletingFrameSubtree(nsFrameManager* aFrameManager, nsIFrame* aFrame) { NS_ENSURE_TRUE(aFrame, NS_OK); // XXXldb Remove this sometime in the future. // If there's no frame manager it's probably because the pres shell is // being destroyed. if (NS_UNLIKELY(!aFrameManager)) { return NS_OK; } nsAutoTArray<nsIFrame*, 8> destroyQueue; // If it's a "special" block-in-inline frame, then we can't really deal. // That really shouldn't be happening. NS_ASSERTION(!IsFrameSpecial(aFrame), "DeletingFrameSubtree on a special frame. Prepare to crash."); do { DoDeletingFrameSubtree(aFrameManager, destroyQueue, aFrame, aFrame); // XXX ... This will enter the recursive function. This function will identify containers and frames that are out of flow and will add them to an argument that's provided by the caller. layout/base/nsCSSFrameConstructor.cpp:7148 static void DoDeletingFrameSubtree(nsFrameManager* aFrameManager, nsTArray<nsIFrame*>& aDestroyQueue, nsIFrame* aRemovedFrame, nsIFrame* aFrame) { #undef RECURSE #define RECURSE(top, child) \ DoDeletingFrameSubtree(aFrameManager, aDestroyQueue, (top), (child)); \ // XXX DoDeletingOverflowContainers(aFrameManager, aDestroyQueue, (top), (child)); // XXX // Remove the mapping from the content object to its frame. nsIContent* content = aFrame->GetContent(); if (content) { aFrameManager->RemoveAsPrimaryFrame(content, aFrame); aFrameManager->ClearAllUndisplayedContentIn(content); } nsIAtom* childListName = nsnull; PRInt32 childListIndex = 0; ... // Queue the out-of-flow frame to be destroyed only if aRemovedFrame is _not_ // one of its ancestor frames or if it is a popup frame. // If aRemovedFrame is an ancestor of the out-of-flow frame, then // the out-of-flow frame will be destroyed by aRemovedFrame. if (outOfFlowFrame->GetStyleDisplay()->mDisplay == NS_STYLE_DISPLAY_POPUP || !nsLayoutUtils::IsProperAncestorFrame(aRemovedFrame, outOfFlowFrame)) { NS_ASSERTION(aDestroyQueue.IndexOf(outOfFlowFrame) == kNotFound, "out-of-flow is already in the destroy queue"); aDestroyQueue.AppendElement(outOfFlowFrame); // XXX // Recurse into the out-of-flow, it is now the aRemovedFrame. RECURSE(outOfFlowFrame, outOfFlowFrame); } else { // Also recurse into the out-of-flow when it's a descendant of aRemovedFrame // since we don't walk those lists, see |childListName| increment below. RECURSE(aRemovedFrame, outOfFlowFrame); } } } One of the recursive functions will add an overflown container to the destroyQueue. layout/base/nsCSSFrameConstructor.cpp:7014 static void DoDeletingOverflowContainers(nsFrameManager* aFrameManager, nsTArray<nsIFrame*>& aDestroyQueue, nsIFrame* aRemovedFrame, nsIFrame* aFrame) { // The invariant that "continuing frames should be found as part of the // walk over the top-most frame's continuing frames" does not hold for // out-of-flow overflow containers, so we need to walk them too. // Note that DoDeletingFrameSubtree() skips the child lists where // overflow containers live so we won't process them twice. const PRBool orphanSubtree = aRemovedFrame == aFrame; for (nsIFrame* next = aFrame->GetNextContinuation(); next && (next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER); next = next->GetNextContinuation()) { DoDeletingFrameSubtree(aFrameManager, aDestroyQueue, // XXX orphanSubtree ? next : aRemovedFrame, next); } } After populating the destroyQueue via the recursive function, the application will iterate through each element and destroy them. This will free the element twice. layout/base/nsCSSFrameConstructor.cpp:7125 // Now destroy any out-of-flow frames that have been enqueued for // destruction. for (PRInt32 i = destroyQueue.Length() - 1; i >= 0; --i) { nsIFrame* outOfFlowFrame = destroyQueue[i]; // Ask the out-of-flow's parent to delete the out-of-flow // frame from the right list. aFrameManager->RemoveFrame(outOfFlowFrame->GetParent(), // XXX GetChildListNameFor(outOfFlowFrame), outOfFlowFrame); } return NS_OK; } Version(s) tested: Mozilla Firefox Platform(s) tested: Windows XP SP3 -- CREDIT -------------------------------------------------------------- This vulnerability was discovered by: * regenrecht
blocking1.9.1: --- → ?
blocking1.9.2: --- → ?
blocking2.0: --- → ?
status1.9.1: --- → ?
status1.9.2: --- → ?
status2.0: --- → ?
I am unable to get this to crash on trunk or 3.6.x on Linux 32-bit... Mozilla/5.0 (X11; Linux i686; rv:2.0b4pre) Gecko/20100812 Minefield/4.0b4pre Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.9pre) Gecko/20100812 Namoroka/3.6.9pre
Keywords: crash
Whiteboard: [sg:critical?]
In FF3.5.x I only see the assertion ###!!! ASSERTION: Nothing to handle this event!: 'frame', file /Users/daniel/dev/ffcentral/moz191/layout/base/nsPresShell.cpp, line 6006 ten times with the PoC, but no crash.
Attached file testcase
With a slight tweak, it is crashing for me, by removing the style of the first styled element. This crashes in 3.6.8, but not in trunk: http://crash-stats.mozilla.com/report/index/0efe3227-7adf-43fa-aa1c-12fe12100813 0 xul.dll nsFrameManager::RemoveFrame layout/base/nsFrameManager.cpp:735 1 xul.dll nsCSSFrameConstructor::ContentRemoved
On trunk, this was fixed between 2009-12-16 and 2009-12-26, I guess bug 508473 fixed this.
On 3.6.9pre I get bp-7aab4c4c-cc7a-4b32-a44c-b1fba2100814 so that's looking like a frame-poisoned crash in 3.6.x I still don't crash in 3.5.x even with Martijn's modified testcase.
Whiteboard: [sg:dos] frame-poisoned crash
Not blocking a 1.9.2 release if it's not exploitable. Appears WFM on 2.0 (I agree w/Martijn in comment 4 that bug 508473 looks like a likely fix). Unsure whether 3.5 is truly unaffected or if it's just masked and could be exposed through a different testcase.
blocking1.9.1: ? → ---
blocking1.9.2: ? → -
blocking2.0: ? → ---
Keywords: crash
Version: unspecified → 1.9.2 Branch
Alias: ZDI-CAN-883
Alias: ZDI-CAN-883
Depends on: 508473
Whiteboard: [sg:dos] frame-poisoned crash → [sg:dos] frame-poisoned crash, fixed on trunk by 508473
Alias: ZDI-CAN-883
We decided bug 508473 is out of scope for the 1.9.2 branch to fix a DoS so not much point to tracking this as a branch bug. Fixed on trunk, wontfix on branch.
Status: NEW → RESOLVED
Closed: 14 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla2.0
Version: 1.9.2 Branch → unspecified
Group: core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: