A specially crafted web-page can cause MSIE 11 to interrupt the handling of one
readystatechange
event with another. This interrupts a call to one of the
various C<ElementName>Element::
functions to make another such call and
at least one of these functions is non-reentrant. This can have various
repercussions, e.CMapElement
object, a reference to that object can be stored in a linked list
and the object itself can be freed. This pointer can later be re-used to cause
a classic use-after-free issue.
Microsoft Internet Explorer 11
An attacker would need to get a target user to open a specially crafted web-page. Disabling JavaScript should prevent an attacker from triggering the vulnerable code path.
When a DocumentFragment
containing an applet element is added to the DOM, all
elements receive a notification that they are removed from the CMarkup
.
Next, they are added to the DOM and receive notification of being added to
another CMarkup
. When the applet is added, a CObjectElement
is created and
added to the CMarkup
. This causes a readystatechange
event to fire, which
interrupts the current code. During this readystatechange
event, the DOM may
be modified, which causes further notifications to fire. However, elements in
the DocumentFragment
that come after the applet element have already received
a notification that they have been remove from one CMarkup
, but not that they
have been added to the new one. Thus, these elements may receive another
notification of removal, followed by two notifications of being added to a
CMarkup
.
AFAICT, this event-within-an-event itself is the root cause of the bug and
allows memory corruption in various ways. I discovered the issue because the
code in CMapElement::
does not handle this sequence of events well.
The below pseudo-code represents that function and shows how this can lead to
memory corruption:
This code maintains a singly linked list of map elements that have been added to the document. An object should never be added to this list twice, as this will cause a loop in the list (a map element pointing to itself as the next in the list). However, the event-within-an-event can be used to first cause two consecutive calls to remove the same element from this list followed by two calls to add the same element to the list. This results in the following sequence of events:
At this point, an attacker can remove the CMapElement
, causing the code to
try to remove it from the list and free it. However, because of the loop in the
list, the above code will not actually remove it from the list. After this,
the pointer in the list points to freed memory.
I focused on the CMapElement::
code and was able to reuse the freed
memory originally used for the CMapElement
with another object of similar
size (eg. a CParamElement
, which may be extra useful as it will store a
pointer to its parent CObjectElement
at offset 38). However, I could not
think of a way to use the CMapElement::
code to do anything useful at
that point. I could also not immediately find any other code that uses this
linked list, which is a bit odd: why would MSIE keep a linked list and not use
it? I suspect there must be other code that uses it, and that this code may
allow exploitation of this vulnerability.
Aside from the use-after-free bug that exists for CMapElement
objects above,
there may be many other issues for other types of objects, as there are many
different C<ElenentName>Element::
implementations for the various
elements. It is assumes that none of these were designed to be reentrant.
Unfortunately, I did not have time to exhaustively reverse engineer their code
to look for other code paths that might be exploitable. As a result I am unable
to prove exploitability beyond reasonable doubt.