Closed Bug 576078 (ZDI-CAN-852) Opened 11 years ago Closed 10 years ago

FrameManager Remote Code Execution Vulnerability (ZDI-CAN-852) (-moz-column, position:absolute)

Categories

(Core :: Layout, defect)

1.9.2 Branch
defect
Not set
critical

Tracking

()

RESOLVED FIXED
Tracking Status
blocking2.0 --- -
status2.0 --- unaffected
blocking1.9.2 --- .13+
status1.9.2 --- .13-fixed
status1.9.1 --- unaffected

People

(Reporter: reed, Assigned: mats)

References

(Blocks 1 open bug)

Details

(Keywords: regression, Whiteboard: [sg:dos frame-poisoned][regressed by 411835, dupe of 526217])

Attachments

(1 file)

Attached file PoC
ZDI-CAN-852: Mozilla Firefox FrameManager 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 implementation of a particular CSS
style when applied to a frame. If an element's contents are replaced
without having a parent, the application will access memory that has
been freed when trying to contact the parent container. This can lead to
code execution under the context of the application.

The bug is triggered when a root frame containing an absolutely
positioned element is removed. Upon removal, the application will
attempt to notify the parent frame to remove the target. This will cause
the application to access the non-existent parent frame and attempt to
fetch a method. Due to the parent frame being freed, this will trigger
an exploitable condition.

layout/base/nscssframeconstructor.cpp:7215
nsresult
nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
                                      nsIContent* aChild,
                                      PRInt32     aIndexInContainer,
                                      RemoveFlags aFlags,
                                      PRBool*     aDidReconstruct)
{
...
      // Recover childFrame and parentFrame
      childFrame = mPresShell->GetPrimaryFrameFor(aChild);
      if (!childFrame || childFrame->GetContent() != aChild) {
        // XXXbz the GetContent() != aChild check is needed due to bug
135040.
        // Remove it once that's fixed.
        frameManager->ClearUndisplayedContentIn(aChild, aContainer);
        return NS_OK;
      }
      parentFrame = childFrame->GetParent();
      parentType = parentFrame->GetType();
...
    // See if the child frame is an out-of-flow
    if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
      nsPlaceholderFrame* placeholderFrame =
        frameManager->GetPlaceholderFrameFor(childFrame);
      NS_ASSERTION(placeholderFrame, "No placeholder for
out-of-flow?");

      // Now we remove the out-of-flow frame
      // XXX has to be done first for now: for floats, the block's line
list
      // contains an array of pointers to the placeholder - we have to
      // remove the float first (which gets rid of the lines
      // reference to the placeholder and float) and then remove the
      // placeholder
      rv = frameManager->RemoveFrame(parentFrame,
                                     GetChildListNameFor(childFrame),
                                     childFrame);

      // Remove the placeholder frame first (XXX second for now) (so
      // that it doesn't retain a dangling pointer to memory)
      nsIFrame* placeholderParent = placeholderFrame->GetParent();
      ::DeletingFrameSubtree(frameManager, placeholderFrame);
      rv |= frameManager->RemoveFrame(placeholderParent,
                                      nsnull, placeholderFrame);     //
XXX
    } else {
      // Notify the parent frame that it should delete the frame
      // check for a table caption which goes on an additional child
list with a different parent
      nsIFrame* outerTableFrame; 
      if (GetCaptionAdjustedParent(parentFrame, childFrame,
&outerTableFrame)) {
        rv = frameManager->RemoveFrame(outerTableFrame,
                                       nsGkAtoms::captionList,
                                       childFrame);
      }
      else {
        rv = frameManager->RemoveFrame(parentFrame, nsnull,
childFrame);
      }
    }

When removing the frame, the application will call the method from
already freed aParentFrame object.

layout/base/nsFrameManager.cpp:719
nsresult
nsFrameManager::RemoveFrame(nsIFrame*       aParentFrame,
                            nsIAtom*        aListName,
                            nsIFrame*       aOldFrame)
{
  PRBool wasDestroyingFrames = mIsDestroyingFrames;
  mIsDestroyingFrames = PR_TRUE;

  // In case the reflow doesn't invalidate anything since it just
leaves
  // a gap where the old frame was, we invalidate it here.  (This is
  // reasonably likely to happen when removing a last child in a way
  // that doesn't change the size of the parent.)
  // This has to sure to invalidate the entire overflow rect; this
  // is important in the presence of absolute positioning
  aOldFrame->Invalidate(aOldFrame->GetOverflowRect());

  nsresult rv = aParentFrame->RemoveFrame(aListName, aOldFrame);    //
XXX

  mIsDestroyingFrames = wasDestroyingFrames;

  return rv;
}

Version(s)  tested: Mozilla Firefox 3.6.3
Platform(s) tested: Windows XP SP3

-- CREDIT --------------------------------------------------------------

This vulnerability was discovered by:
    * wushi of team509
Summary: FrameManager Remote Code Execution Vulnerability (ZDI-CAN-852) → FrameManager Remote Code Execution Vulnerability (ZDI-CAN-852) (-moz-column, position:absolute)
blocking1.9.1: --- → ?
blocking1.9.2: --- → ?
blocking2.0: --- → ?
status1.9.1: --- → ?
status1.9.2: --- → ?
status2.0: --- → ?
bp-1bc12835-254c-470b-8181-376322100630

Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.7pre) Gecko/20100630 Namoroka/3.6.7pre
Can't get this to crash on trunk... Maybe some other change fixed this already?

Mozilla/5.0 (X11; U; Linux i686; en-US; rv:2.0b2pre) Gecko/20100630 Minefield/4.0b2pre
Are you bisecting to figure out what, or should somebody else?
(In reply to comment #3)
> Are you bisecting to figure out what, or should somebody else?

Somebody else should, please.
Before the crash I get:

###!!! ASSERTION: out-of-flow is already in the destroy queue: 'aDestroyQueue.IndexOf(outOfFlowFrame) == kNotFound', file /Users/jruderman/moz192/layout/base/nsCSSFrameConstructor.cpp, line 7104

Since this bug's crash signature is [@ nsFrameManager::RemoveFrame], it could be the same as bug 526217, which was fixed on trunk in bug 508473.
That seems to be the case, since it was in fact fixed between the 2009-12-23-03-mozilla-central and 2009-12-24-03-mozilla-central Linux x86_64 nightlies.
Maybe we should consider porting bug 508473 to the 3.6 branch?
I couldn't get 3.5.x to crash (on mac). Is the problem merely masked or did we introduce it along the way?

The 3.6 crash looks like a frame-poisoned address -- are we really vulnerable?
bp-ae09d31b-19cd-4795-a122-4ad952100630
zwol thought 3.5.x continuing to work might be a bad sign -- maybe we used a dead but not overwritten frame? But I ran it in a debug build and still had no problems.
Clearing sg:critical pending an answer to the frame-poisoning question in comment 9.
Whiteboard: [sg:critical?]
Whiteboard: [sg:critical]
blocking1.9.1: ? → needed
blocking1.9.2: ? → needed
No longer depends on: 536721
Whiteboard: [sg:critical] → [sg:critical?] frame-poisoning DoS on 1.9.2+
Depends on: 536721
Version: unspecified → 1.9.2 Branch
For Firefox 3.5, some alternatives to backporting bug 508473:
* Backport frame poisoning
* Disable -moz-column
* Accelerate EOL (it's been 6 months since Firefox 3.6's release)
Whiteboard: [sg:critical?] frame-poisoning DoS on 1.9.2+ → [sg:critical?] frame-poisoning DoS on 1.9.2+ [critsmash:investigating]
cc'ing wu shi
from today's triage session we summarized from commments 9 and 10 that a crash can't be reproduced on 3.5.x so not exploitable there, and 3.6.x crashes at a frame poisoned address so no exploit there either.   roc may have more, but does that summary sound correct?
This doesn't affect trunk, so not blocking.
blocking2.0: ? → -
Whiteboard: [sg:critical?] frame-poisoning DoS on 1.9.2+ [critsmash:investigating] → [sg:dos (critical w/out frame-poisoning)][mysteriously, can't reproduce on 1.9.1][critsmash:investigating]
This does look like a dupe of Martijn's bug 526217, which was a regression from bug 411835. That never landed on the 1.9.1 branch which explains why it's unaffected.
Blocks: 411835
blocking1.9.1: needed → ---
Whiteboard: [sg:dos (critical w/out frame-poisoning)][mysteriously, can't reproduce on 1.9.1][critsmash:investigating] → [sg:dos (critical w/out frame-poisoning)][regressed from 411835, dupe of 526217?][critsmash:investigating]
Alias: ZDI-CAN-852
Alias: ZDI-CAN-852
Alias: ZDI-CAN-852
Whiteboard: [sg:dos (critical w/out frame-poisoning)][regressed from 411835, dupe of 526217?][critsmash:investigating] → [sg:dos frame-poisoned][regressed by 411835, dupe of 526217]
Fixed by bug 526217.
Status: NEW → RESOLVED
Closed: 10 years ago
Resolution: --- → FIXED
Assignee: nobody → matspal
(In reply to comment #18)
> Fixed by bug 526217.

er, I mean bug 468563.
setting blocking to match bug 468563 (also, ZDI is already expecting this to be fixed in the December release)
blocking1.9.2: needed → .13+
Group: core-security
Issue is resolved - clearing old keywords - qa-wanted clean-up
You need to log in before you can comment on or make changes to this bug.