Software components such as memory managers often use magic values to mark memory as having a certain state. These magic values can be used during debugging to determine the state of the memory, and have often (but not always) been chosen to coincide with addresses that fall outside of the user-land address space on 32-bit versions of the Operating System. This can help detect vulnerabilities by causing an access violation when such magic value is used as a pointer as well as mitigate exploitation of such vulnerabilities by making it impossible to have this "poisoned" pointer refer allocated memory under the attacker's control.
For instance, Microsoft's C++ debugging runtime library initializes stack
memory to 0xCCCCCCCC
. When an uninitialized object pointer is used to read
the value of a property or call a method of the object, this reliably causes an
access violation on 32-bit versions of Microsoft Windows and prevents an easy
path to exploitation.
The Wikipedia article on magic values has a list containing some of the
values and when they are used. You will notice how all of the values used on
Windows have their high bit set (i.
On 64-bit architectures, there is no need to reserve part of the 32-bit address space for kernel memory. Consequently, a 32-bit applications running on 64-bit versions of Windows is able to allocate memory in almost the entire 32-bit address range. This allows 32-bit applications to allocate more memory, including at all addresses that these magic values can reference. Ever since their introduction over 10 years ago, Javascript heap-sprays in web-browsers in particular offers an attacker the ability to finely control memory allocations and their content for use in exploits.
Last year I stumbled upon two different bugs in two different web browsers where a magic value was used to mark memory, and subsequently used as a pointer. Using Javascript, it was possible to allocate memory at the address based on the magic values the web browsers were using and store information at this location to exploit both of these two vulnerabilities. These issues have both been address, so I can discuss them in more detail here.
Mozilla 1088635 covers a bug in Firefox that could it to use data from a
freed and "poisoned" object through specially crafted HTML, which resulted in
access violations around address 0x5a5a5a5a
on x86 systems. The memory used
to back the object was marked with this magic value after it was freed. Because
this magic value resulted in an address can be allocated even on 32-bit
versions of Windows, I suggested in Mozilla 1182002 that the value get
updated with something that makes it a little harder to exploit. This and other
reasons for changing the magic values, led to magic values being
changed to 0xe4e4e4e4
for uninitialized memory and 0xe5e5e5e5
for freed
memory.
<script>
window.onload = function() {
oMenuElement = document.createElement("MENU");
document.open();
document.insertBefore(oMenuElement, null);
document.write("<script></"+"script>\n");
};
</script>
The repro caused the nsHtml5TreeOperation
's mOne
member object to be freed.
The freed memory was not immediately released, but poisoned by filling it with
"z" (0x5a). A pointer to the freed object is kept in mOne
and continued to be
used. The repro caused xul!nsHtml5TreeOperation::
to pass the node
member of mOne
to xul!nsHtml5TreeOperation::
as a pointer to an
object, but because the pointer's value was 0x5a5a5a5a
, this caused an access
violation.
It should have been fairly straightforward to exploit this issue using a run-of-the-mill heap-spray when the magic value was still 0x5a5a5a5a. but after the magic value was changed to the new, higher values, the issue can no longer be exploited on 32-bit versions of Windows, as memory cannot be allocated at addresses starting with 0xe4 or 0xe5 on 32-bit versions of Windows.
In August last year, I found what appear to be a thread-safety vulnerability
in Google Chrome when handling audio data, that could lead to use of an
uninitialized pointer. This issue is only visible when running Chrome with
page heap enabled, as the memory used to store the pointer appears to be
set to 0x00000000
after allocation when page heap is not enabled. This means
this NULL pointer will not be used by the code to reference memory. However,
when running Chrome with page heap enabled, the pointer will be initialized to
0xD0D0D0D0
and gets used in code that allows at least freeing of arbitrary
memory pointers.
<!doctype html>
<html><head>
<script>
new AudioContext("notification").decodeAudioData(
new ArrayBuffer(1),
function(){},
function(){}
);
location. reload();
</script></head></html>
The repro triggers a race condition in the AudioOutputDevice thread, where the thread is terminated before a pointer to fiber local storage (FLS) is initialized.
When the thread is terminated and the FLS pointer is not NULL, the FLS pointer
is passed to msvcrt!_freefls
, which assumes it points to a structure with
some more pointers. For a number of pointer in the structure, it calls
msvcrt!free
if they are not NULL.
An attacker could use a heap spray to allocate memory around address
0xD0D0D0D0
and have the uninitialized pointer point to whatever data is
useful. When the msvcrt!_freefls
function is called, it will process that
attacker supplied data.
The above code allocates a Uint32Array that is so large it will only fit in the
(assumed) unused area above 0x80000000
. This causes it to be reliably
allocated at address 0x80004000
. Using this array, a pointer at 0xD0D0D0D0 +
0x24
is set to 0xDEADBEEF
. When msvcrt!_freefls
is called with the
uninitialized FLS pointer, it will check if the pointer at 0x24
is not NULL
and call msvcrt!free
to free the memory it points to. This leads to an
attempt to free memory at address 0xDEADBEEF
, which should result in a crash.
After doing a more thorough analysis, Ricky Zhou explained to me in the
Chromium bug that the issue is not in Chromium, but in verifier.
.
This DLL is used to implement page heap on Windows. The problem is that in
verifier!AVrfDebugPageHeapAllocate
, the HEAP_ZERO_MEMORY
flag is sometimes
ignored, which in this case caused the memory to get initialized with the wrong
value. I reported this issue to Microsoft at the end of October last year and
after getting the MSRC case number 31596, I did not hear back from them again
until after I reached out again when I was writing this blog. Microsoft
informed me that they consider this attack scenario too limited to warrant a
security updated.
While working on these issues, I realized that this type of attack is easy to mitigate by making sure the magic values point to memory that has been reserved and marked inaccessible. That way there is no risk of an attacker allocating the memory with data under his/her control for use in an exploit: whenever the application would attempt to access memory using a magic value as a pointer, this would reliably cause an access violation.
Having a memory allocation at the various addresses represented by common magic
values fragments the address space, reducing the largest possible continuous
allocation and the total amount of memory available to the application. But
most 32-bit applications do not depend on being able to allocate such large
chunks of memory for normal operations, as this is impossible on 32-bit
versions of Windows. Regardless, should one want to prevent this fragmentation,
and at the same time organize the magic values to be more coherent and
intuitive to developers, it might be useful to create an API that can be used
to generate the magic values, and have the generated values be more similar,
closer together and/or located at either edge of the free memory above address
0x80000000
. It might make particular sense to use addresses towards the
higher end (0xFFxxxxxx
) as adding a large enough offset to overflow the 32-bit
address would have a large chance of resulting in a NULL pointer; which would
still cause an access violation.
Such a "magic value API" could implement this mitigation during start-up of a process, and reserve a chunk of the address space for use with magic values. The API then allows the application to request magic values which would be sequential addresses at the start of the reserved memory region, so that when an offset gets added to them, the resulting address would still be very likely to fall within in. This API would provide different 32-bit magic values, which may not always be desired, for instance in the case of the Firefox memory allocator, which fills memory with one specific byte, repeated over and over. The API could therefore also allow the application to request a byte, at which point the API would find an address consisting of the same four bytes and reserve it before returning it. This would increase memory fragmentation, but not by much (assuming the number of magic values needed is in the order of two or three, as is the case with Firefox).
I have suggested adding mitigations such as this API to MSRC and they responded in November 2015 that they forwarded it to the Windows team for their consideration.
Obviously, this type of mitigation may be of interest to authors of security software that aims to mitigate exploitation of 0-day: it should be possible to: