Operation Aurora

The news was alive in January 2010 with talk of how Google was thinking of
a new approach to China, possibly including to withdraw its business, after
what was said to be a “highly sophisticated and targeted attack on our corporate
infrastructure originating from China” in December 2009. Early technical analysis,
including to publish that the attack exploits a vulnerability in Internet Explorer
that had been unknown to the security industry, seems to have been in the hands
of MacAfee, whose page
Operation Aurora Hit Google, Others christens the attack and has George Kurtz
effusing that “the world has changed” and that “everyone’s threat model now needs
to be adapted to the new reality of these advanced persistent threats”.

Statements like that easily get my attention since I have been saying for years
that reverse engineering of malware as practised by the computer security industry
is primitive and seems likely to stay that way since the industry has little or
no incentive to invest in improvement. The industry must eventually face malware
whose complexity, if not sophistication, exceeds the industry’s capacity for analysis.
Though I certainly don’t want malware to get better at what it does, that it will
get better is just inevitable, and I can’t help but await the industry’s game-changing
moment when it realises that reverse engineering is not a hobby, or even an occasional
tool, but is more like a performance art that requires years of dedicated practice
(which few would ever think to commit to without support).

For a reverse engineer from outside the industry, the first difficulty in studying
an attack is with getting the code that was used for the attack. This was made public,
sort of, a few days after the initial reports. Although several pages on the Internet
talked of relevant code having “been published on the Internet”,
Attack Code Used to Hack Google Now Public is the first I know that said where.
It provided a URL at the
Wepawet malware analysis website. The code given there is not syntactically
valid and has a space that’s in the wrong place. Though these minor errors were
presumably introduced while formatting the code for presentation, there is the additional
problem that no provenance is given: it could be any code from anywhere for any
purpose.

Still, the working assumption must be that people at the targeted companies were
invited to visit a web page whose HTML is very similar to what’s posted at the Wepawet
site. The script in this HTML triggers a bug in Internet Explorer after arranging
an increased probability that Internet Explorer does not crash in response to the
bug but is instead induced to execute the attacker’s bootstrap code (which is also
in the script). This in turn downloads an executable file from a preset website
and then runs that executable.

Some few practical points ought be noted at the outset. First, although the bug
is present in very nearly a decade’s worth of Internet Explorer versions, and can
crash all these versions, the reported attack hardly ever gets as far as trying
to execute the attacker’s code if the Internet Explorer version is 7.0 or higher.
Variations of the attack might, of course.

Since the bootstrap code is provided as script, it is handled by Internet Explorer
only as data. If Data Execution Prevention (DEP) is enabled, then if the attack
does succeed at subverting the bug so that Internet Explorer does not crash, DEP
will crash Internet Explorer for you rather than let it unwittingly execute data.
DEP requires at least Windows XP SP2 or Windows Server 2003 SP1 (if we don’t count
undocumented AMD-specific support in the original Windows Server 2003). Really,
you should have DEP enabled, always. Any software that is incompatible with DEP
is better replaced or discarded.1

If you disable Active Scripting, then Internet Explorer will never interpret
any script. As used in the attack, the bug cannot then cause Internet Explorer to
crash, let alone to be exploited. You are entirely safe from the attack. Against
that, your experience of browsing the web will be very much poorer. Although disabling
scripts protects you from this and other security problems, it cannot sensibly be
recommended as standard practice for everyday web browsing by everyday users. Many
websites will present you with an error message directing you to enable scripting.
Some will just present you with a blank page. Many more will misbehave in various
ways because they don’t so much require scripting for additional functionality as
assume it for basic functionality. An example is Microsoft’s MSDN site, which presents
its programming documentation without a scrollbar so that you can’t read the whole
text unless you enable scripting. Indeed, the widespread practice of writing web
pages that take scripting for granted is largely Microsoft’s responsibility: Microsoft
evidently writes its websites without much thought for the security-conscious, and
it’s no surprise that numerous other websites follow Microsoft’s lead.

Anything that can be done in script can be done by executable code delivered
to Internet Explorer as an ActiveX control. Although this attack uses script, other
attacks that exploit the same bug may use ActiveX controls. Of course, if you’re
security conscious, you will have disabled ActiveX controls long before wondering
whether Active Scripting is something to disable too. You will also have noticed
that many websites expect you to run ActiveX controls, if not actually to download
them, and that most of these websites degrade significantly if you decline to let
them execute their choice of code on your computer.

Finally, Internet Explorer the web browser is not just an application program.
The essential DLLs of Internet Explorer act as operating-system components that
provide HTML functionality to a wide variety of other applications. Since the bug
is in one of those DLLs, it can also crash these other programs or induce them to
run an attacker’s code on your computer. Unfortunately, the list of such programs
is open-ended since the hosting and reuse of Internet Explorer components has long
been documented by Microsoft as a significant programming feature. The good news
is that most such programs don’t take as input an arbitrary HTML page received over
the Internet. The ones that do typically don’t run scripts or ActiveX controls,
but you may do well to check.2

Some forensic points may better be noted, too. This article is concerned only
with what can be known about the bug and its exploitation from the publicly disclosed
script and from inspection of Internet Explorer. Where reports describe this attack
as a sophisticated operation, they must be talking about things that happen before
or after the attack. By before, I mean the planning of who to target and how to
entice them to a web page that would run the script that launches the attack. No
amount of inspecting code can help with that, and it is conspicuous that very little
information of any sort is in the public domain to support any talk of how the attack
was targeted. By after, I mean whatever is done by the downloaded executable, which
could be any Trojan and can have been developed independently of the attack. I have
no copy of the Trojan for inspection. By the time I looked at the preset URL to
get the Trojan, there was no file there to download and the root of the website
was just the default page of a newly installed IIS7 website.

I could, of course, get whatever Trojan has been seen in the attacks. Copies
are surely in circulation. What puzzles me, though, and what I want to concentrate
on, is how the attacks ever got as far as downloading a Trojan. How was the attack
delivered to its victims, what is the Internet Explorer bug that allowed the attack
to work, and how did this bug get exploited? On these questions, curiously little
detail has been made public.

The vulnerability is an Internet Explorer memory corruption issue triggered by
an attacker using JavaScript to copy, release, and then later reference a specific
Document Object Model (DOM) element.

Fortunately for any notion of independent evaluation, someone has published HTML
code that was supposedly seen in real-world exploitation of this same vulnerability.
The code can be read by anyone who’s interested and most who do read it will surely
get at least an inkling of what’s going on, particularly that the “specific DOM
element” must be the event object.

Strictly speaking, the event object is an
IHTMLEventObj4 interface implemented by MSHTML.DLL
in a tear-off thunk for an instantiation of the CEventObj
class. Though the CEventObj in effect
is the event object, it
does not hold the event properties directly. These are
in an EVENTPARAM structure. Most events have a source
element, accessible as the srcElement property. However
the srcElement is not stored directly in the
EVENTPARAM as a pointer to the element’s representation
as a CElement
class. Instead, the EVENTPARAM has a pointer to the
element’s node in the DOM tree. This is also true, incidentally, for the
fromElement and toElement
properties, though these are meaningful only for a couple of event types. Each tree
node has a pointer to its element and vice versa. The tree nodes themselves seem
intended as internal to MSHTML. The CTreeNode class is
not a COM class. Indeed, it has no virtual member functions of any sort. It does
have ordinary member functions that give it the appearance of its element and which
perform reference counting even though the class does not hold its own reference
count. It is this last point that gives us the present bug.

There can be no doubt that if CTreeNode were a COM
class, then any programmer given charge of MSHTML would always be on the lookout
for the possibility that a tree node’s AddRef method
needs to be called before a pointer to that tree node can safely be set into any
structure whose lifetime is not certainly contained in the lifetime of the node.
Instead, although the CTreeNode class does have a
NodeAddRef member function, and does get it used, pointers
to tree nodes are often passed around without adding a reference. It may be that
this was once-upon-a-time well supported by some close understanding of the relevant
objects’ lifetimes, but it nowadays looks in need of a rethink.

Certainly, there is a problem when an EVENTPARAM
is copied, as can be done in either of this structure’s constructors. The input
EVENTPARAM may already have non-NULL pointers to nodes
for the srcElement, fromElement
and toElement. The new EVENTPARAM
receives copies of these pointers to the nodes but without any adding of references.
The nodes and their elements then know nothing of these copies. This is a problem
if the new EVENTPARAM outlives the original. When the
original EVENTPARAM is no longer needed once the event
it was created for has been handled, if the node is no longer wanted in the tree,
then the node will be freed even though the second EVENTPARAM
still has a pointer to it.

That is the essence of the reported bug. It dates from MSHTML version 5.50. This
is the first version to have EVENTPARAM constructors
that can copy another EVENTPARAM. It is also the first
to provide, via the document object’s
createEventObject method, a means of getting an
EVENTPARAM copied just by using script in a way that
the copy can be made to outlive the original.

Demonstration

If we aim to trigger the bug entirely in script, then the following must be near
to minimal.3 Just
insert this script as the whole of a <script> block
which is in turn the whole of an HTML page’s <body>:

This script creates an HTML element, inserts it into the DOM tree, sets an event
handler for the element and then fires an event on that element. The handler uses
the document object’s createEventObject
method to clone the given event object, which is then
saved in a global variable so that it outlives the event. Cloning the given
event has the internal effect of copying the event’s
EVENTPARAM structure, including its pointer to the
CTreeNode for the source element. The handler then removes
this source element from the tree. By the time the event has finished, the
CTreeNode is freed from memory but a pointer to it persists
in the EVENTPARAM structure for the saved
event object. The script ends by asking for the
srcElement property of this object, knowing that MSHTML
can evaluate this property only by using this stale pointer.

If you want that even the <body> should be empty,
then move the <script> block to the head after recasting
so that what’s presently done in global variables is instead the body of a function
that is certain to execute when the document has loaded:

If I dwell on this, it’s to make the point that this is a bug that can be triggered
just by visiting an empty web page—not just one that has something hidden, but one
whose body truly is completely empty.

If you prefer to test for this bug immediately, rather than cut and paste into
a page at your own web server, click on the Show Demonstration button below to reveal
some similar script and a Run Demonstration button that will run that script from
this page. Do not click on that second button unless you are prepared for Internet
Explorer to crash!

[Note added 29th July 2015] The demonstration that was here has
been removed temporarily. When left in place, for this analysis to retain the expression
I intend, this site's hosting provider, Fluccs,
quarantines the whole page because their virus scanner, CXS,
insists the page is infected by something called JS.Dropper-33. The cited malware
is apparently an in-the-wild exploit of the vulnerability that is examined here.
Of all the malware in the world that might have infected this one page! Clearly,
the scanner has produced a false positive, though I don't yet know if it has been
merely lazy or too clever. The demonstration will be restored after experiments
establish what the makers of this scanner will permit me to write.

What Sort of Fix?

As noted above, this is not one of those bugs that is caused just by a line or
two in the source code and which might be fixed for the moment by patching a few
bytes in the executable (pending a proper update from the manufacturer). If I am
right, MSHTML has a potentially wide-ranging problem with its reference counting
of nodes. Any update that doesn’t obviously look like having dealt with this ought
not be accepted as a fix.

For instance, it will inevitably cross someone’s mind to react to this bug by
disabling the createEventObject method, presumably as
an option, perhaps implemented as yet another of the many
Internet Explorer feature controls.
It may be that this method isn’t much missed. However, it may be that
createEventObject actually is vital to some web pages,
which I expect it is for pages that simulate user actions, e.g., for tutorials on
using web pages. Remember that the standard DOM model for events, despite significant
differences from Microsoft’s, also provides for creating event objects. The functionality
is not so obviously obscure that it won’t be missed if disabled as a security measure.
More importantly, disabling createEventObject looks
like shooting the messenger. Although createEventObject
is the easy and obvious means of triggering the bug, it might not be the only means.
Probably the only way to be sure would be to look at all ways that
EVENTPARAM structures can get copied, paying particular
attention to lifetimes and to the reference counting of nodes whose addresses ever
get entered into EVENTPARAM structures. If anyone at
Microsoft is going to do that, then they may as well fix the reference counting
so that createEventObject works well enough that nobody
should fear it.

For as long as MSHTML has EVENTPARAM constructors
that can copy another EVENTPARAM without the caller
having to know or care about the contents of the input EVENTPARAM,
these constructors must be changed so that they call the NodeAddRef
method for each node that is already pointed to from the input
EVENTPARAM. They do, of course, already call the
AddRef method of each COM object that may be pointed
to from the given EVENTPARAM. The wonder is that doing
the equivalent for the nodes has been missed.

Not only is it necessary to fix the EVENTPARAM constructors,
it’s also sufficient if all that’s wanted is a quick and dirty way to escape this
bug. By doing just this, all nodes whose addresses ever get copied into an
EVENTPARAM by an EVENTPARAM
constructor can never get freed in error—but neither can they ever get freed when
they ought to be. The bug would go away but at the price of an ever-growing number
of unwanted nodes that remain in memory for no good reason. Microsoft will surely
present a proper solution before anyone feels compelled to resort to any such quick-and-dirty
solution.

For a proper solution, calls to add a reference must be balanced by calls to
release the reference. Unfortunately, it’s not enough just to add corresponding
calls to NodeRelease in the EVENTPARAM
destructor. Pointers to nodes are not set into EVENTPARAM
structures only by the constructors. Of these other cases, some call
NodeAddRef for the node, and some do not. For some,
a NodeAddRef will be needed, and for some not. Similar
variability applies to releasing nodes. I expect that although the right handling
is easily enough prescribed, changing existing code to conform to the prescription
requires careful inspection, which is perhaps not even feasible at short order except
by someone who has been immersed in the MSHTML code for a long time.

This is perhaps as good a place as any to bring out another hobby horse. Another
inevitability with a bug like this is that some will say Internet Explorer has unusually
many bugs like this because Microsoft does not open its source code to public inspection.
Whether they’re right with their assessment, I don’t know, but I doubt they can
be right with the explanation. Any difficulty with reading software like MSHTML
without its C++ source code, especially if aided by symbol files, is nothing compared
with the huge investment required for understanding the code, which must run to
millions of lines. I have not spent a working life immersed in the MSHTML code.
Who has and who would want to? I may be way off track with my analysis—but I expect
I could as easily be as far off track even if I had the source code. Moreover, having
source code could take me off track in a different direction and give me a false
security about it. Most of what you can get from reading source code but can’t get
from reading the binary code (with symbol files) is the commenting—and in code that
is known to have allowed a bug to lie undiscovered for a decade, the comments are
plausibly part of the problem.

Microsoft’s Fix

As it turns out, I’m no more off track than are Microsoft’s own programmers.
On 21st January (U.S. time), Microsoft released a cumulative security update as
Microsoft Security Bulletin MS10-002 - Critical which “resolves seven privately
reported vulnerabilities and one publicly disclosed vulnerability in Internet Explorer”.
The latter is apparently the bug being examined here, identified in the update’s
description as CVE-2010-0249.

I do not propose to check all the downloadable packages for the various combinations
of Internet Explorer and Windows versions. For this analysis I have looked only
at how the update applies to what I happen to have the best notes on, namely the
Internet Explorer 7 from the original (x86) Windows Vista. The corresponding update
provides six different MSHTML.DLL executables for installation as assemblies in
the WinSxS directory. The executable that gets installed in the SYSTEM32 directory,
and is used when Internet Explorer is run, is version 7.0.6000.16982. Anything I
say here about Microsoft’s fix is based on comparison of that version with the version
7.0.6000.16386 from the original Windows Vista:

The new build inserts new code into the two EVENTPARAM
constructors so that they call NodeAddRef for each non-NULL
pointer that the input EVENTPARAM has for the
srcElement, fromElement
and toElement properties.4
A new global function standardises the setting of pointers to nodes. Acting much
as for smart pointers, this function ensures that when a pointer is set to the non-NULL
address of a node, a NodeAddRef is called for the new
node. Correspondingly, any node that was held in the pointer gets a
NodeRelease. The EVENTPARAM
destructor has new code that uses this new function to clear the
srcElement, fromElement
and toElement pointers. Assuming that this new function
is now used exclusively when setting pointers to nodes into
EVENTPARAM structures, Microsoft has indeed straightened out the reference
counting of tree nodes with regards to their storage in EVENTPARAM
structures.

The bug is therefore fixed. But look at the date stamp for the new MSHTML. Though
Microsoft’s security advisory for this bug, published on 14th January 2010, never
put a date to the “attacks … using a vulnerability in Internet Explorer” let alone
to Microsoft’s knowledge of that vulnerability, the date stamp for the updated MSHTML
puts beyond dispute that Microsoft had not only known of the vulnerability nearly
a month earlier but had fixed it. Moreover, Microsoft had already fixed the bug
not as an update just for the latest Internet Explorer version but for each of three
versions. Is parallel correction Microsoft’s usual practice for bugs in Internet
Explorer or is it a sign that Microsoft regarded the bug as critical even in mid-December
2009?

I’m not saying that Microsoft is in any sense at fault for taking months to fix
a bug, just that Microsoft’s official publication to the world, meaning the security
advisory, might better have provided a less empty historical record from the start.
That has been left instead to a Microsoft blog. At
Bulletin MS10-002 Released, dated 21st January, Jerry Bryant explains that

… this Internet Explorer security update was already planned for release in February.
When the attack discussed in Security Advisory 979352 was first brought to our
attention on Jan 11, we quickly released an advisory for customers three days
later. As part of that investigation, we also determined that the vulnerability
was the same as a vulnerability responsibly reported to us and confirmed in early
September.

Perhaps only the naive would imagine that any large software manufacturer could
move faster than that, or ought to. After all, until Microsoft learnt that the vulnerabilty
actually was being exploited for a security attack, it was just a bug that can crash
Internet Explorer just for looking at a page. No biggie! Five or six months is apparently
what Microsoft ordinarily plans for correcting such bugs. If we didn’t know before,
we know now.

More contentious may be the question of how a newly discovered bug percolates
through Microsoft’s processes to be evaluated as a security vulnerability. As noted
above, Microsoft itself refers to this bug as
CVE-2010-0249, which is its number in a public list of Common Vulnerabilities
and Exposures. Its (candidate) entry in that list is dated 7th January 2010, i.e.,
before Microsoft learnt that the bug had found real-world use in an attack but months
after Microsoft learnt of the bug itself.

While speculating on things that can’t be known through any amount of inspecting
either Microsoft’s code or the attacker’s, let me draw attention to one more point.
This bug’s life has run for nearly 10 years. If attackers have the capacity to learn
of bugs independently of Microsoft, then they might have learnt of this bug at any
time in those 10 years. Did that happen, and the attackers sat on their discovery,
waiting to put it to use? Or did they just by fluke discover the bug (and how to
exploit it) simultaneously with the bug having been “responsibly reported” to Microsoft?

Exploiting the Bug

A minimal expectation of all modern operating systems is that when a program
uses a pointer to memory that has been freed (and perhaps reused), the overwhelmingly
most probable consequence is that the program soon does something that the CPU detects
as invalid, prompting the operating system to crash the program. To turn overwhelming
probability to certainty is at least unrealistic if not actually impossible. For
instance, unless a memory manager arranges the luxury of never reusing memory that
has been freed, memory that once held data for one instance of some class might
again hold data for another instance of exactly that same class. Though the program
thinks erroneously that it is still working with the first instance, its code may
reasonably be satisfied with what it finds from the second instance. The only sign
that anything is wrong might be that bad results are reported to the user because
the program has confused one instance for the other. Of course, operating systems
have a role in minimising such possibilities, but there is only so much that can
be done. There is unavoidably some very slight possibility that by the time a program
uses memory that has been freed, what is still in this memory or has since been
put there (e.g., by reusing the memory) will be just right enough that the error
is not caught and does not crash the program.

Cyber-attackers live for this sort of marginal possibility. Especially attractive
to them is that they may be able to manipulate what’s at the freed memory so that
they not only stop the program from crashing but can instead divert the program
to execute the attacker’s choice of code. That their imaginations can realistically
reach so far is because many programs make very heavy use of pointers to functions.
The DLLs of Internet Explorer, especially MSHTML, must be some of the heaviest users
around of pointers to functions. Very many memory allocations by MSHTML hold a COM
object (or a tear-off thunk for a COM object) containing at least one pointer to
a virtual function table, each of which is itself an array of pointers to functions.

Brute-Force Theory

Because MSHTML is so rich in pointers to functions, brute force can work wonders
for the attacker. Between the time that the program frees some memory while keeping
a pointer to it and the time that the program uses that stale pointer to try accessing
the memory, the attacker may be able to flood the program’s free heap space with
pointers. The pointers may all have the same target address, but this is enough
for the attacker’s first goal. When the program accesses the previously freed memory,
there’s a very good chance that it is looking to find a pointer in this memory.
If this setting up of the attack succeeds, there’s a good chance that what the program
finds in this memory is one of the attacker’s pointers. When the program follows
this pointer to whatever it wants to work with next, it will still be executing
its own code but it will be looking where the attacker wanted it to be looking.
The attacker who gets this far has made a really good start. Problems remain of
what to store at the target address and how to get it there, but these are relatively
easy.

To see what would be useful at the target address, whatever may yet be chosen
for that address, consider what the program must be looking for if exploitation
is to be possible. The attacker hopes that the program is looking for a pointer
to a function, or at least to a pointer to other memory where the program may in
turn look for a pointer to a function: in general then, a chain of pointers, possibly
each with an offset, ending with a pointer to a function. The easiest way to catch
this generality is to make the chain circular, as long as there’s an exit. Allowing
for offsets at each pointer in the chain, the attacker will want to put at the target
address an array of pointers to the target address. When the program reaches the
end of the chain and follows what it thinks is a pointer to a function, it will
start executing at the target address and thus try executing the array of pointers
as if they are the opcode bytes of CPU instructions. If the attacker can find a
target address whose repetition makes for a meaningful opcode sequence, he has his
second goal: the program is now executing his choice of code.

Inspection of the x86 opcode tables shows numerous sequences that don’t exactly
do nothing but which don’t do very much. What counts as not very much for the attacker
is very broad since he does not care what changes are made to the CPU’s general
registers. Pretty much any instruction sequence that doesn’t risk a CPU fault by
trying to access memory will do. The attacker can let this sequence execute repeatedly
for as long as he wants for his array, and then follow with the code that he really
does want to execute, i.e., his bootstrap code for the attack.

The first candidate in the x86 opcode tables is 00 C0, which just adds the
al register to itself. However, this sequence corresponds
to a target address of C000C000. On Windows, this address is necessarily outside
the user-mode address space. This brings us to the attacker’s last concern: he needs
a target address that offers a realistic chance of being able to get the pointer
array stored there. The next candidate in the opcode tables is 04
xx. This just adds the byte
xx to the al register.
The corresponding target address is xx04xx04.
This is a good candidate since it (and many similar) can be adjusted to suit whatever
turns out to be possible as an address that the attacker can store at. All the attacker
has to do is choose a large enough xx so that the
corresponding target address is greater than any that is ever seen at the lower
end of the user-mode address space in typical usage. Though DLLs are loaded near
the top of the user-mode address space, memory for the program’s data requirements
tends to be allocated upwards. The attacker can prepare a large pointer array followed
by bootstrap code, as one block, and then get the program to store this block as
many times as needed until the new allocations reach the target address. If the
bootstrap code is neglibly small relative to the pointer array, then it is almost
certain that where the target address is contained within one of these newly added
blocks, it will fall in the pointer array, as wanted. The attacker has all that’s
needed to exploit the use-after-free bug, and has not needed to understand the particular
bug in any sort of detail.

The “Operation Aurora” Exploitation

Now that we have the theory for a brute-force exploitation, we may usefully look
at the HTML code that is supposed to have been used in the attacks reported by Google.
As noted, the published code cannot actually be correct, not that this has stopped
it being reproduced in what are said to be analyses. I have cleaned it up very slightly:

Look first at the use of createEventObject in the
event handler ev1. The event is an
onload from an img element
in the body. As in the minimal demonstration given
earlier, the event handler saves the created event object
(as e1) and then removes the event’s source element
from the DOM tree. A callback ev2 is then arranged
so that more script gets to run after the event has been handled. That much should
be familiar by now as setting up the bug. The callback ends with the bug being triggered.
All the rest of the script is for exploiting the bug.

The script from the beginning until the use of the JScript function
eval is where the attacker gets Internet Explorer
to store what the attacker wants at the chosen target address. The script is a little
obfuscated, but its effect is build an array of 200 members which each consist of
the bytes for a large pointer array followed by the bootstrap code which is provided
as the sc variable. The target address for each pointer
is 0C0D0C0D. Each member is set as a string literal of 512K Unicode characters.
Thus, Internet Explorer is made to find 200 memory blocks of 1MB each. Assuming
upwards allocation from low memory, this is a little more than should be needed
so that one of the blocks contains the target address.5

The setup part to a successful brute-force exploitation is much the more difficult
in practice. Some time after the node is erroneously freed but before it is accessed,
the attacker must flood the heap’s free space with pointers to the target address
in the hope that the memory that used to hold the node (and which MSHTML still thinks
holds the node) will now get overwritten with the attacker’s pointers. This is the
idea behind building the array x1 before the event
and modifying it in the callback. Presumably in the hope that MSHTML uses the same
area of memory all the while that it works with elements and nodes, the array is
built as a collection of HTML elements. The callback function sets new values for
a simple property of each element. The new value is supplied as a string but is
equivalently an array of pointers to the target address. Although the attacker can’t
control where MSHTML stores the new values, the hope is that one of them will just
happen to overwrite the memory that had been used for the freed node.

Note, by the way, that the attackers seem not to have very much idea why the
setup part of their attack should work. All other things being equal, you would
expect that the optimum size to write in the hope of overwriting a freed node would
be the size of the node. If you write more, you risk that the heap manager allocates
from a different heap segment (for bigger blocks) or anyway can’t allocate where
you want because memory both before and after the freed node remains allocated.
For most MSHTML versions, each CTreeNode is 0x30 bytes,
rising to 0x4C in version 8.0. Yet the attackers write 0x54 bytes.

Evaluation

The setup part of the exploitation works very successfully for MSHTML version
6.0, but markedly less well in version 7.0 and higher. The difference is explained
entirely by a change of heap manager.

MSHTML version 6.0 has its own small-block heap, similar to the one in the C
Run-Time Library (CRT), for blocks that are smaller than 0x0230 bytes. The process
heap is used only for larger blocks. One practical consequence is that requests
for small heap blocks by MSHTML version 6.0 are met from an area of memory that
is used only by MSHTML. Since MSHTML has nothing much to do except interpret the
script, the node that was freed after handling the event will very likely still
be free for the callback. The attacker’s hope expressed above is sound for MSHTML
version 6.0.

MSHTML version 7.0 changes this by using the process heap whatever the size of
block. Where script hopes to set new contents for memory that previously held a
node, it now has to compete with all the other users of memory in the whole of Internet
Explorer, i.e., with all the other DLLs, executing in all the other threads. A lot
happens throughout Internet Explorer between the “execution” of one script statement
and the next, and the attacker can’t control this. The memory that held the erroneously
freed node will much less likely be still free for the callback. Even if it is,
the heap manager may by then be allocating from a different area of memory. The
brute-force attempt to overwrite the freed node with pointers to the target address
can only rarely succeed.

This is not to say that no variation exists that works occasionally, and perhaps
even more often than not, with later MSHTML versions, but it is to say that MSHTML’s
change of heap manager makes it much harder to design a brute-force attack in script
that can expect much success at inducing MSHTML to try executing the attacker’s
code. Whether Microsoft had security in mind when changing the heap manager is anyone’s
guess, but if they did, they’ve been quiet about it when describing why Internet
Explorer 7 and 8 are less susceptible.

By the way, where Microsoft’s Security Bulletin for the bug fix talks of Address
Space Layout Randomization (ASLR), what’s said is not wrong—it may protect against
variant attacks that would exploit this same bug—but it is not the slightest bit
applicable to this particular exploitation. It’s only a small point in this case,
but the wider question is whether the public interest is well served if a software
manufacturer’s default response might be to promote security features without checking
that they actually do apply.

It’s more than merely odd that an attack at the end of 2009 depends on exploitation
code that experiments readily show to be far more successful on Internet Explorer
versions from three and more years before. The attackers must have known they had
next to no chance of success except with users of Internet Explorer 6. Were they
content with this because they could determine which browsers are used by potential
targets and found that Internet Explorer 6 is used by enough of these targets to
make the attack worthwhile? Without such intelligence about their targets, why even
think to attack Google, which makes its own browser, such that few staff are likely
to use any version of Internet Explorer let alone an old one?

The Bootstrap Code

Having got control of Internet Explorer’s execution, the attacker can do pretty
much anything but is limited at first because only so much code can sensibly be
included with the exploitation itself. In practice then, the exploitation arranges
to execute a relatively small amount of bootstrap code. The job of this code is
just to load something larger, typically from a server on the Internet, and to get
it running as a program on your computer. What happens after that could be anything—literally,
because the attackers control the server that responds to the bootstrap code’s requests
for a program to download. They can vary this as they like: the program seen by
one observer does not have to be the same as downloaded to another.

The bootstrap code supplied with the reported attack, i.e., with the HTML page
reported on the Wepawet website, has a hard-coded URL from which to download one
file and has a simple notion of where to download this file to:

Source:

http://demo1.ftpaccess.cc/demo/ad.jpg

Destination:

AppData\a.exe

Here, AppData represents the shell folder denoted
by CSIDL_APPDATA. This will typically be

Despite the names, this downloaded file is not expected to be either a picture
file or an executable. All that the bootstrap code knows about this file is that
it should be an executable that has been disguised, such that an XOR by 0x95 is
needed for every byte that is not already 0x95 or 0x00. The decrypted file is rewritten
as “b.exe” in the same directory, the “a.exe” file is deleted, and the “b.exe” file
is executed. It can be whatever the attacker chooses to send.

Whether the file downloads or not, the bootstrap code tries to keep the Internet
Explorer process alive. The code cannot put right the problems that led to its being
executed. Instead, it tries to restart Internet Explorer’s execution—within its
current process, not as a new process—by calling the IEWinMain
function, as exported by SHDOCVW.DLL. Perhaps to cover for this not working, the
bootstrap code first patches the USER32 function MessageBeep
with the effect that execution of this function, presumably to indicate an error,
will terminate the process.

This attempt to restart Internet Explorer by calling IEWinMain
is suspect not just for whether it is reliable in principle but also for the details
of how this particular code attempts it. For instance, although the code uses the
StackBase member at offset 0x04 in the
fs segment to determine where to restore the stack
pointer to match (roughly) what this function would expect in ordinary circumstances,
it does nothing for the ExceptionList at offset 0x00
in the fs segment. Even an exception that Internet
Explorer expects to handle will then cause trouble.

Most interesting however is that the coding does not account for important changes
introduced for Internet Explorer 7 (and Windows Vista). In these versions, the essence
of Internet Explorer is not SHDOCVW but IEFRAME (which cuts large tracts from the
old SHDOCVW and BROWSEUI, and copies much from SHLWAPI). If the bootstrap code means
to keep Internet Explorer running, the IEWinMain that
it would need to call is the one in IEFRAME. Though SHDOCVW continues to export
an IEWinMain function, it is just a forward to a stub
in SHUNIMPL.DLL for trivial failure. That this might not be known to any supposed
author of this code in 2009 is scarcely credible. Either this code was written in
2009 explicitly for execution on Internet Explorer 6 or this code actually does
predate Internet Explorer 7. Whichever is true, both ought give the security industry
pause for thought.

[1] Sadly, such
software does exist, even from Microsoft. One example is the Document Explorer
program that accompanies the Microsoft Developer Network (MSDN) Library on disc.
Version 7, as supplied with editions from 2003 and 2004, fails to start if DEP
is enabled. Fortunately for those who are curious to know what Microsoft wrote
about DEP ahead of releasing Windows XP SP2, this old documentation is readable
using version 8.

[2] One such
program hides in plain sight on every Windows machine. It is Microsoft’s HTML
Help tool, HH.EXE, which displays CHM files. Though HH is not a web browser, it
will open an arbitrary HTML or CHM file, using MSHTML for rendering, and will
run JavaScript from that file. An HTML file with script could be compiled into
a CHM file and sent in email as an attachment. Opening the attachment will run
Internet Explorer by proxy. A special danger with this line of attack is that
the users who are most exposed are those who prefer other browsers, e.g., because
they think Internet Explorer is insufficiently secure. With no direct reason to
update the Internet Explorer that they don’t want (and whose presence they may
even resent), they are the most likely to have an old version whose known security
vulnerabilities have not been fixed. Fortunately, CHM files are regarded as unsafe
attachments and are blocked by most email readers.

[3] The demonstration
can be made a little smaller. Instead of creating a child element on which to
fire an event, just fire the event on the body. However, removing the body from
the DOM tree is artificial to the point that it might confuse about what exactly
is necessary for the bug.

[4] The new
code for the EVENTPARAM constructors also deals with
other data members, namely the VARIANT for the
returnValue and the CStr
structures for the type,
propertyName, qualifier and
srcUrn properties, that were previously copied byte
by byte. I do not know if this change was made simply as a natural reconsideration
of how the constructors are coded, or whether it was prompted by observation of
ill effect, or even whether such ill effect can be arranged in builds that predate
the bug fix.

[5] That the
particular target address for this attack is unaligned is perhaps a coding error
by the attacker. Not that there’s much consequence to it in practice, but it means
that the pointer arrays must cover two addresses rather than one: 0C0D0C0D and
0D0C0D0C.

This page was created on 18th January 2010 and was last modified
on 11th December 2011.