The blog continues at suszter.com/ReversingOnWindows

December 28, 2013

Hardcoded Pointers

Use of hardcoded pointer could enable the attacker to bypass ASLR. In this draft I'm describing potential methods to find a hardcoded pointer in your target.

When exploiting particular vulnerabilities it is fundamental to read/write or jump to predictable memory location in the process' address space. ASLR randomizes the memory locations of various key locations including addresses of libraries. Even though we see that some high profile applications still load libraries with ASLR disabled, we have high hopes they will fix the problem soon.

That wouldn't solve the problem overall though. Applying ASLR to all libraries does not mean there is not easily predictable locations in the process' address space. There are API functions that accept address to allocate memory at that address. These functions can be used to hardcode memory address, and so to assign a fixed address to a pointer (CWE-587). As a consequence, it gives an attacker a chance to read/write or jump to known address to bypass ASLR.

For these functions you can specify the desired starting address that you want to allocate. When doing security audit it's worth checking if the functions are called with hardcoded addresses.
VirtualAlloc
VirtualAllocEx
VirtualAllocExNuma
MapViewOfFileEx
MapViewOfFileExNuma
The following functions accept address to read as parameter. These are not appear to be useful but leave them for potential future use.
UnmapViewOfFile, WriteProcessMemory, ReadProcessMemory, FlushViewOfFile, FlushInstructionCache, Toolhelp32ReadProcessMemory, GetWriteWatch, ResetWriteWatch, ReadProcessMemoryProc64, VirtualUnlock, MapUserPhysicalPages, VirtualProtect, VirtualProtectEx, VirtualQueryEx, GetFrameSourceAddress, CompareFrameDestAddress, VirtualFree, VirtualFreeEx, FindNextFrame, WSPStringToAddress, CompareAddresses, AddressToString

It's also worth checking if the application you audit uses shared memory as some application map the memory at fixed address, and even boost library supports the use of this insecure method.
The use of relative pointers is less efficient than using raw pointers, so if a user can succeed mapping the same file or shared memory object in the same address in two processes, using raw pointers can be a good idea. To map an object in a fixed address, the user can specify that address in the mapped region's constructor:
mapped_region region ( shm                         //Map shared memory
                     , read_write                  //Map it as read-write
                     , 0                           //Map from offset 0
                     , 0                           //Map until the end
                     , (void*)0x3F000000           //Map it exactly there
                     );
When auditing source code for hardcoded address it's worth looking for constant starting with 0x ending with 0000 as some might indicate hardcoded memory address. I wrote a simple batch script for that.

The another batch script I have is for binary code. I recommend to use if you don't find a bug using other methods. To use it you need to execute dasmdir.py on the binary file to produce disassembly, and you may run the batch script on it to get the immediate values filtered.

This is interesting. Here is an example of someone asking how to allocate memory at fixed address unintentionally making his software less secure.

December 11, 2013

Flash Player Unloading Vulnerability II

UPDATED 20/February 2014: This vulnerability is CVE-2013-5332 and technical details were publicly announced on 10th December 2013 after the vendor patch was released to end users. On 19th of February 2014 I unexpectedly received an e-mail from HackerOne saying "The Internet Bug Bounty has awarded you a $2000.00 bounty for Flash memory corruption vulnerability could lead to code execution".

Adobe has notified me that it addressed the Flash Player Unloading vulnerability I reported them earlier. The patch is now released to end users and the security bulletin is published which contains the nature of the vulnerability.

In this post, I describe the technical details of the vulnerability in the hope that software developers can learn from it.

In the Windows version of Safari, when a Flash file is opened, the browser initiates to load the Flash Player into the process address space. When the Flash file is no longer opened the browser initiates to unload the Flash Player.

The Flash Player can display a dialog box using MessageBox API. While the dialog box is active, an other thread can unload the Flash Player module from the address space. When the execution returns back from the MessageBox call to the Flash Player module, which is already unloaded, the instruction dereferences freed memory causing data execution prevention exception.

This is a use-after-free vulnerability and it is same in nature to the one I reported Adobe earlier however this time the vulnerability is triggered via a different code path. The crash state looks like below, and it suggests the issue is most likely exploitable for code execution.
(3860.37ec): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=523bb781 ebx=00000001 ecx=006f0d68 edx=00000000 esi=523bb781 edi=00000000
eip=523bb781 esp=001deb90 ebp=001debb8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210206
<Unloaded_NPSWF32_11_7_700_224.dll>+0x20b781:
523bb781 ??              ???
0:000> |.
.  0          id: 3860               attach    name: C:\Program Files (x86)\Safari\Apple Application Support\WebKit2WebProcess.exe
One possible fix would be to synchronize the DLL unloading thread with the code calling MessageBox.

The vulnerability I reported Adobe earlier is same in nature to this one. Also, if a developer purely adds a new MessageBox call he has a high probability to inherently create a new vulnerability that can be reached in the same way described in this post. Therefore, for Adobe, it would be beneficial to review the entire code base for unsafe dialog box calls.

Also, for Microsoft, hardening on OS level is worthy to consider which may involve to crash in a not exploitable manner when a bug is triggered. The problem looks to be more ubiquitous than the public vulnerability reports suggest. The approach to discover these issues is to identify dialog box and DLL unloading code fragments that can be manually attacked.

Since there may be many bugs that could end up dereferencing unloaded Flash Player, Mozilla long time back fixed the problem by permanently keeping the plugin in the memory in Firefox process.

I'm currently working on tools to identify attack surfaces. It involves to identify DLLs calling MessageBox, and identify ways to unload MessageBox DLLs from the process address space.

I created a sample implementation to represent the vulnerability including how the freed memory gets dereferenced. It is a good exercise for developers to think about how to make the code secure without removing the thread creation.
// This is the EXE file.
// Main thread loads the DLL file to call ShowMessageBox() export that calls MessageBox().
// Secondary thread frees DLL but the dialog box is still visible.
// When OK is clicked the freed memory is dereferenced.
#include <Windows.h>

static HMODULE handle;

DWORD WINAPI Thread(LPVOID lpParam)
{
   printf("[Thread] Waiting 5 seconds.\n");
   Sleep(5000);
   FreeLibrary(handle);
   printf("[Thread] FreeLibrary() called. Click OK to dereference freed memory.\n");
   return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
   typedef void (WINAPI *FUNC)(void);
   FUNC ShowMessageBox;

   handle = LoadLibrary(L"DialogDLL.dll");
   printf("LoadLibrary() called.\n");

   ShowMessageBox = (FUNC)GetProcAddress(handle, "ShowMessageBox");

   CreateThread(NULL, 0, Thread, NULL, NULL, NULL);
   printf("Thread created for FreeLibrary().\n");

   printf("MessageBox pops up.\n");
   ShowMessageBox();
   return 0;
}


// This is the DLL file.
// It has an export that calls MessageBox()
extern "C" __declspec( dllexport ) void ShowMessageBox();

void ShowMessageBox()
{
    MessageBox(NULL, L"...shown by the DLL that can be unloaded while this dialog box is still visible.\n\nThe unloaded DLL
can be dereferenced when OK is clicked.", L"This is a dialog box...", MB_OK);
}
The complete source code for Visual C++ 2010 can be downloaded from here. When executing the program it looks like below.

When OK is clicked the instruction dereferences freed memory.
Just a note that the control transfer instruction to dereference freed memory is not call that can be seen in v-table bugs but ret.

July 29, 2013

Using Pintools to Detect Bugs

Recently I spent some time to get into Pin and to explore how feasible to write Pin-based tools to detect programming errors. Here is the summary of my experiment. I think it could be useful for someone who might want to write Pintools.

I had been thinking of dynamic ways to catch programming error without making the detection logic complex. My decision was to write a tool that can detect division by zero errors when the division is performed by a function argument. The tool works as follows.

The tool inspects division instructions that are reading stack via EBP.
   if (INS_Opcode(ins) == XED_ICLASS_IDIV || INS_Opcode(ins) == XED_ICLASS_DIV)
   {
[...]
      if (INS_IsStackRead(ins) && INS_RegRContain(ins, REG_EBP) && INS_IsMemoryRead(ins))
      {
         INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)TrackDivisionByArg, IARG_INST_PTR, IARG_MEMORYREAD_EA, IARG_REG_VALUE, REG_EBP, IARG_END);
      }
Since the function arguments are stored at EBP+XX it's possible to detect when a division is performed by a function argument.
VOID TrackDivisionByArg(ADDRINT inst_ptr, ADDRINT memoryread_ea, ADDRINT reg_ebp)
{
   // Do we read argument of the function (EBP+XX)?
   if (memoryread_ea >= reg_ebp)
   {
[...]
However, this doesn't mean there is a division by zero error because there might be a test for zero. To filter out obvious false positives the tool inspects if the parameter is accessed before the division.
   else if ((INS_Opcode(ins) == XED_ICLASS_CMP || INS_Opcode(ins) == XED_ICLASS_MOV) && INS_IsStackRead(ins) && INS_RegRContain(ins, REG_EBP) && INS_OperandIsImmediate(ins, 1))
   {
      INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)TrackAccessToArgs, IARG_INST_PTR, IARG_MEMORYREAD_EA, IARG_REG_VALUE, REG_EBP, IARG_REG_VALUE, REG_ESP, IARG_END);
   }
The tool checks the functions on isolation so if the function parameter is checked for zero by the caller the program may report division by zero error. This is the consequence of the design.

The tool uses std::map data structure to maintain the state of the analysis. It also contains other research implementation that is not mentioned in this blog post. The source code is available to download here.

If you use std::map functions you have to use mutex and write lock to protect the data structure from potential corruption unless the target uses no threads.

To get better performance it's better to do analysis once the application exists rather than on-the-fly. However, this increases the memory footprint as the data can be accumulating during the execution.

Sometimes it might be a good idea to use Window Beep function to make noise if a bug is detected.

While it's possible to write efficient Pintools to detect bugs, sometimes if the tool is made to depend on the target application it can perform better.

June 24, 2013

Sample Pintools for Visual Studio

Creating a Visual Studio Project for Pintools (32-bit)

  • Start Visual Studio 2010 and create a new Win32 Project.
  • In the Win32 Application Wizard, select DLL for Application type, and tick Empty project in the Additional options.
  • Create directory Pin in the Solution folder and copy Pin framework here.
  • Copy Pin\source\tools\ManualExamples\inscount0.cpp to the Project folder. This is a sample Pintools file. Add the copied inscount0.cpp to the project.
  • Set Active Configuration to Release.
  • Add the following to the Additional Include Directories:
$(SolutionDir)Pin\source\include\pin;$(SolutionDir)Pin\source\include\pin\gen;$(SolutionDir)Pin\extras\xed2-ia32\include;$(SolutionDir)Pin\extras\components\include;%(AdditionalIncludeDirectories)
  • Add the following to the Additional Library Directories:
$(SolutionDir)Pin\ia32\lib;$(SolutionDir)Pin\ia32\lib-ext;$(SolutionDir)Pin\extras\xed2-ia32\lib;%(AdditionalLibraryDirectories)
  • Add the following to the Preprocessor:
TARGET_WINDOWS
TARGET_IA32
HOST_IA32
  • Set Enable C++ Exceptions to No, and set Runtime Library to Multi-threaded (/MT).
  • Add the following to the Additional Dependencies in the Linker settings:
ntdll-32.lib
libxed.lib
pin.lib
pinvm.lib
libcmt.lib
libcpmt.lib
  • Set Ignore All Default Libraries to Yes (/NODEFAULTLIB).
  • Set Entry Point to Ptrace_DllMainCRTStartup@12.
  • Add the following to the Additional Options of the Command Line linker option:
/EXPORT:main

Downloading&Building Sample Pintools for Visual Studio

  • To download Sample Pintools Project created with the above settings click here [SkyDrive].
  • Open the solution file in Visual Studio and set the active configuration to Release.
  • Create directory Pin in the Solution folder and copy Pin framework here.
  • Copy Pin\source\tools\ManualExamples\inscount0.cpp to the Project folder. This is a sample Pintools file.
  • Build the project.

April 15, 2013

Tracing Thread ID of ModLoad and ModFree Events

This log was created by an experimental Windbg extension that is to trace the thread ID of ModLoad/ModFree events. When a module is being unloaded the extension queries the ID of the current thread and compares to the thread ID that loaded the module. If two different threads used to load and to unload the module the extension issues a notification as seen in yellow below.
ModLoad 59440000 00000454          PRNFLDR
ModFree 59440000 00000454 00000454 PRNFLDR
ModLoad 59440000 00000454          prnfldr
ModLoad 74b90000 00000454          WINSPOOL
ModLoad 5aa30000 00000454          prncache
ModLoad 74950000 00000454          RpcRtRemote
ModLoad 673a0000 00000454          actxprxy
ModLoad 6b180000 00000454          ieproxy
ModLoad 5a9f0000 00000454          thumbcache
ModLoad 5fcc0000 00000454          ieframe
ModLoad 75800000 00000454          api-ms-win-downlevel-ole32-l1-1-0
ModLoad 74490000 00000454          api-ms-win-downlevel-shlwapi-l2-1-0
ModLoad 74470000 00000454          api-ms-win-downlevel-advapi32-l2-1-0
ModLoad 6b250000 00000454          api-ms-win-downlevel-shell32-l1-1-0
ModFree 5aa30000 0000055c 00000454 prncache
-->prncache (5aa30000) is allocated by thread id 454 but freed by 55c
ModLoad 55250000 00000454          NPSWF32_11_7_700_169
ModLoad 771d0000 00000454          urlmon
ModLoad 6c310000 00000454          DSOUND
ModLoad 744a0000 00000454          POWRPROF
ModLoad 735a0000 00000454          mlang
I observed that normally the same thread is responsible to load and to unload the module. However, that's not always the case. If you can force a context switch on the code path of dereference, and to get the unloading thread to trigger ModFree, you could end up to dereference freed memory.

April 4, 2013

Browsers Could Enable to Plant Malware

Today, Opera released a patch which attempts to address one of the issue I reported them in February, 2013.

The most likely attack is that a remote attacker may trick a user to visit a specially crafted web page; and to perform undisclosed operation (social engineering). As a result, a malicious executable file may be planted on the user's computer.

Either social engineering or a second bug is required to plant malware, so it's reasonable to assign moderate severity to the issue.

Another, probably less likely attack vector includes that an attacker has physical access to the machine. When the file downloads are blocked by security policy it may be easily possible for the attacker to bypass it and plant malware. This is a risk, specially in a corporate environment.

Firefox (Bug 845880), Chrome (Issue 177980), and Safari (Follow-up: 260250675) are affected.

Internet Explorer 10 is not affected. I concluded they recognized this issue internally and implemented defense.

I haven't verified Opera's patch yet.

Technical description scheduled to be posted later this year.

April 2, 2013

Integer Overflow and Memory Failures in WavPack

The common consequences of the integer overflow are denial-of-service and out-of-bounds memory access. What I'm describing here is neither of them but rather a rare consequence I discovered recently.

WavPack is an open source project providing audio compression and decompression. The project consists of the library and utilities exercising the library. The library compresses and decompresses Wav and WavPack streams. It's used in several software (e.g. WinZip) and hardware. One of the utilities called WVUNPACK decompresses the WavPack file.

I'm discussing a bug in WVUNPACK that is not shipped with the 3rd party products using WavPack library, and so the bug doesn't affect 3rd party products but WVUNPACK only.

WVUNPACK has an undocumented command line switch k that allows the user to control the size of the buffer to operate with. This switch expects a number to calculate the size of the buffer with.
case 'K': case 'k':
    outbuf_k = strtol (++*argv, argv, 10);
outbuf_k is int, and its value is controlled by the user. Assume the user calls WVUNPACK specifying -k4194303 in the command line. The integer after k is copied to outbuf_k. Note, 4194303 is 0x3fffff.

The program calculates the size of the buffer to operate with. However, there is an integer overflow when calculating output_buffer_size and it becomes 0xfffffc00 after the execution of the code snippet below.
if (outbuf_k)
    output_buffer_size = outbuf_k * 1024;
The program attempts to allocate memory with 0xfffffc00. The allocation fails, and the returning pointer that is NULL is not sanity-checked.
output_pointer = output_buffer = malloc (output_buffer_size);
Without detecting the memory allocation failure, the execution continues and the decompression is starting by creating an output file and writing a header in it.
if (!DoWriteFile (outfile, WavpackGetWrapperData (wpc), WavpackGetWrapperBytes (wpc), &bcount) ||
The program unpacks the content of the file to a temporary buffer.
samples_unpacked = WavpackUnpackSamples (wpc, temp_buffer, samples_to_unpack);
However, the block within the if statement is not reached because output_buffer is NULL, and so the decompressed data is not written to the output.
if (output_buffer) {
[...]
        if (!DoWriteFile (outfile, output_buffer, (uint32_t)(output_pointer - output_buffer), &bcount) ||
To recap the issue, the integer overflow causes that the unpacked data is not written to output and there is no error displayed believing the unpacking is successfully completed.
The screenshot below demonstrates there is no error displayed but when manually checking the file size there is a mismatch.
In addition to that, WVUNPACK supports to calculate and to display MD5 signature to verify the output. This can be enabled by m command line switch. This check is performed on the temporary buffer that is never written to output, therefore the error remains undetected. In fact, the program could display the correct MD5 while the output has different checksum.

Here is the code snippet demonstrates MD5 calculation is performed on temporary buffer.
if (calc_md5 && samples_unpacked) {
[...]
    MD5Update (&md5_context, (unsigned char *) temp_buffer, bps * samples_unpacked * num_channels);
And here is the screenshot demonstrates the bug in checksum verification.
The bug described above is found in WVUNPACK, however, I'd like to provide information about the library, too, for those use it in 3rd party products. According to WavPack website the library is used in several hardware including jukeboxes, multimedia and network players, and in many software including VLC Media Player.

WavPack library, probably for performance reasons, doesn't check the return value of memory allocation functions. This looks safe when investigating the library on isolation as those seem to work with small buffers and so it's difficult to make the allocation to fail, and to possibly enter in vulnerable paths. However, the library is widespread and used in different systems, and in different software environment, it could even possibly run in browser process. Earlier this year, I proved how to make allocation fails with fixed or small size.

Few examples could access near NULL:
orig_data = malloc (sizeof (f32) * ((flags & MONO_DATA) ? sample_count : sample_count * 2));
memcpy (orig_data, buffer, sizeof (f32) * ((flags & MONO_DATA) ? sample_count : sample_count * 2)); 
[...]
wps->blockbuff = malloc (wps->wphdr.ckSize + 8);
memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader));
[...]
riffhdr = wrapper_buff = malloc (wrapper_size);
memcpy (wrapper_buff, WavpackGetWrapperLocation (first_block, NULL), wrapper_size);

March 13, 2013

Flash Player Unloading Vulnerability

Opera unloads Flash Player after two minutes of not being used. Flash Player can create dialog that can run even when the player itself is unloaded. When it's unloaded, the running dialog makes a call back to the unloaded module, in a time window.

If an attacker can exploit the time window between the unload and the dereference, it's possible to redirect the execution flow.

While experimenting, I used heap spray to inject 0xCC bytes to the deleted memory. I succeeded once of many attempts and this suggests it's practically possible to create an exploit that could inject malicious code within the time window.

When investigated this vulnerability, I wanted evidence to the question: what module unloads the Flash Player? I used Windbg to place breakpoint at unload events. I let the application to trigger the unload events, and when the debugger hit the breakpoint, printed the call stack.
0:014> sxe ud
0:014> g
Unload module C:\Windows\SysWOW64\Macromed\Flash\NPSWF32_11_4_402_287.dll at 54760000
eax=00000000 ebx=035f5584 ecx=00000000 edx=00000000 esi=035f5bc8 edi=00000000
eip=77b1fc72 esp=0037dab0 ebp=0037db30 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200246
ntdll!ZwUnmapViewOfSection+0x12:
77b1fc72 83c404          add     esp,4
0:000> kc
ntdll!ZwUnmapViewOfSection
ntdll!LdrpUnloadDll
ntdll!LdrUnloadDll
KERNELBASE!FreeLibrary
WARNING: Stack unwind information not available. Following frames may be wrong.
Opera_534b0000!OpSetSpawnPath
Opera_534b0000!OpWaitFileWasPresent
I saw the Flash Player was freed by Opera indeed.

I needed the answer for the question also: what module created the thread that calls back to the unloaded module? Here are the series of Windbg commands used to get the answer.
sxn et
sxn ud
bp CreateRemoteThreadEx "kc; gu; !handle eax 8; gc"
g

KERNELBASE!CreateRemoteThreadEx
kernel32!CreateThreadStub
WARNING: Stack unwind information not available. Following frames may be wrong.
NPSWF32_11_4_402_287!native_ShockwaveFlash_TCallLabel
Opera_534b0000!OpGetNextUninstallFile
Opera_534b0000!OpGetNextUninstallFile
NPSWF32_11_4_402_287!NP_Shutdown
NPSWF32_11_4_402_287
NPSWF32_11_4_402_287!DllUnregisterServer
Opera_534b0000!OpWaitFileWasPresent
ntdll!TppPoolReserveTaskPost
ntdll!TppTimerpSet
Opera_534b0000!OpGetNextUninstallFile
Opera_534b0000!OpSetLaunchMan
Opera_534b0000!OpSetLaunchMan
Handle a20
  Object Specific Information
    Thread Id   88ac.1700
    Priority    10
    Base Priority 0
    Start Address 5497083d NPSWF32_11_4_402_287!native_ShockwaveFlash_TCallLabel
ModLoad: 6bda0000 6bdd0000   C:\Windows\SysWOW64\wdmaud.drv
ModLoad: 72cb0000 72cb4000   C:\Windows\SysWOW64\ksuser.dll
ModLoad: 6d9e0000 6d9e7000   C:\Windows\SysWOW64\AVRT.dll
ModLoad: 6cad0000 6cad8000   C:\Windows\SysWOW64\msacm32.drv
ModLoad: 6bd80000 6bd94000   C:\Windows\SysWOW64\MSACM32.dll
ModLoad: 6bd70000 6bd77000   C:\Windows\SysWOW64\midimap.dll
Exit thread 6:3f90, code 0
Exit thread 7:4074, code 0
Unload module C:\Windows\SysWOW64\Macromed\Flash\NPSWF32_11_4_402_287.dll at 54760000
Unload module C:\Windows\system32\WINSPOOL.DRV at 736f0000
(88ac.1700): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000102 ebx=00000000 ecx=75a914d0 edx=00000000 esi=11c8d000 edi=11c8d470
eip=549706c0 esp=0aacfd38 ebp=0aacfd80 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
<Unloaded_NPSWF32_11_4_402_287.dll>+0x2106c0:
549706c0 ??              ???
I saw that thread 88ac.1700 is created by the Flash Player. When the player is unloaded the thread is still running. Finally, the thread calls back to the unloaded module. Therefore, I knew Flash Player created the thread that calls back to its own module that has been unloaded.

And here is some interesting observation.

When started to look into triggering a dialog in Flash Player I created the following ActionScript that executes an infinite loop.
class InfiniteLoop {
static function main() {
           for (var i = 0; i < 1;) {}
}
}
I built the Flash file with the command below.
mtasc.exe -swf InfiniteLoop.swf -main -header 125:125:20 InfiniteLoop.as
The Flash file triggers a slow script warning dialog. I was able to unload and to crash the Flash Player while the dialog was still running. I achieved this with version 11.4.402.278. When reported the problem to Opera they couldn't reproduce it. That time there was a Flash Player update, and I found it out that they used the latest version of Flash Player that was 11.4.402.287. With the latest version, I couldn't reproduce the problem either.

It got me thinking if Adobe fixed security problem either silently or "accidentally". When I checked the release notes of the latest version I didn't see indication of this kind of fix. I still don't know what had happened between the two releases but thought to investigate this issue further. I tried to trigger the bug via different code paths. I looked at my Flash file collection to dig out a file that triggers a dialog. And here it is, with the one I found, I could crash the player with the same call stack as before.

Initially, I thought Opera could fix this issue by leaving the module permanent in the address space, so I reported this to Opera. However, they notified Adobe after confirming it's not the problem in Opera (though they considered it as a stability issue). Adobe received the report at the end of November, 2012. The bug is now fixed in the security update that has a number of APSB13-09 at 12 March, 2013.

The preliminary version of this blog post was reviewed by Adobe. The preliminary version is same to the published version apart from some changes in the wording.

February 18, 2013

Expired Pointers of an Exported DLL Function

Once the dynamic-link library (DLL) is loaded in the memory, the address of an exported function is usually retrieved. The address is copied into a variable. Before the program executes the exported function, it retrieves its address from the variable. A potential vulnerability exists if the DLL is unloaded but the variable still contain the address of the exported function that was previously valid.

Here is one approach to get started the investigation into a potential vulnerability.

Let's assume the debugger executes the target application. We configure it to break-in when a DLL is unloaded. We know the base address and the size of the DLL as the debugger displays it. When the debugger breaks in we search for pointers that fall into this region. We set data access breakpoint for each pointer, and resume the execution to see if the pointers are accessed.

We may find that some of the pointers set to NULL when the breakpoint is triggered. This is most likely to prevent the access to invalid memory.

We may find that other expired pointers get copied into another memory location. This could be suspicious as we don't normally read expired pointers, albeit, often whole structures get copied even if they contain members that are no longer used.

We may find that some of the breakpoints are not triggered, and we need to analyze further if they can be reached from the execution flow.

February 11, 2013

Out of Memory Issues in Internet Explorer 9

Recently, I was examining how Internet Explorer 9 performs under low-memory conditions.

The test involves to consume large amount of memory in the virtual address space of the renderer process. In this circumstance the execution flow can proceed on error condition of the memory allocation call, and so we can test how the application behaves when the memory allocation fails.

I used the following Windbg script as a template to exhaust the memory of the renderer process. I run the tests with between 16M and few hundred megabytes of free memory in the virtual address space.
$$ Exhausts the memory in the virtual address space. The memory is considered
$$ to be exhausted when an allocation with 16M fails.
$$
$$ Example Usage: $$>< e:/exhaust.wds
$$
$$ Last Updated: 10/February/2013
r $t0 = 0x40000000;
.while (@$t0 >= 0x100000)
{
   r $t1 = 0;
   .echo
   .echo "Size     Address";
   .echo "-----------------";
   .while (@$t1 == 0) {
     r $t1 = 1;
     .catch {.foreach /pS 5 (Address {.dvalloc /r @$t0}) { .printf "%08x %08x\n", @$t0, Address;}; r $t1 = 0;}
   }
   r $t0 = @$t0 / 2;
}
I opened local HTML/SVG files that were legitimate rather than fuzzed, and visited random websites under low-memory conditions. During my experiment, I observed many access violations because of the failed memory allocations. Even though most of them are harmless ones (NULL pointer crashes) some ends up to read data from invalid memory addresses that are not NULL.

Out of memory problem could be a security risk, and this is an attack surface in Internet Explorer. A mitigation to handle this issue would be to implement the following wrapper around the function that allocates the memory. If the allocation fails the renderer process stops with int 3 exception, so we avoid enter to any potential vulnerable code path.

As a reference, here are some stack traces of NULL pointer crashes.
eax=16acc320 ebx=16acc320 ecx=7765e38c edx=00629d08 esi=00000000 edi=16acc324
eip=678b34c5 esp=04ac6854 ebp=04ac6858 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210246
MSHTML!TSmartResource<CD2DDCTraits>::Acquire<ID2D1RenderTarget *,enum D2D1_DC_INITIALIZE_MODE,bool>+0x15:
678b34c5 8b06            mov     eax,dword ptr [esi]  ds:002b:00000000=????????
0:005> kb
ChildEBP RetAddr  Args to Child              
04ac6858 678b45bd 00000000 00000000 16dd9af0 MSHTML!TSmartResource<CD2DDCTraits>::Acquire<ID2D1RenderTarget *,enum D2D1_DC_INITIALIZE_MODE,bool>+0x15
04ac686c 678b4498 00000000 04ac689c 00000000 MSHTML!RefCounted<CD2DDCHolder,SingleThreadedRefCount>::Create<IDCHolder,ID2D1RenderTarget *>+0x5a
04ac68e8 678b3f9f 04acd308 04acd404 04ac69e0 MSHTML!CGDIRenderMode::OnBegin+0x37
04ac6914 678b416d 1125d810 04ac69e0 04acd404 MSHTML!CDXRenderTarget::GetDC+0x1e0
04ac6940 678b41ca 04ac6978 04ac69e0 16e55668 MSHTML!TSmartResource<CDispSurfaceDCMode>::Acquire<CDispSurface *,CRect const *>+0x6c
04ac6a68 67ac98b9 04acd268 04ac6c58 04ac6c18 MSHTML!COleLayout::Draw+0xd6f
04ac6a94 67ac9663 04ac6c58 04ac6c18 04acd308 MSHTML!CLayout::DrawClient+0xaa
04ac6d6c 67ac78e9 04acae4c 00000000 00000007 MSHTML!CDispLeafNode::DrawSelf+0x56c
04ac6e84 67ac82d0 169fe050 00000000 00000007 MSHTML!CDispNode::Draw+0x2c8
04ac6ea8 67ad9c5e 00000000 04ac6f74 04acae4c MSHTML!CDispContainer::DrawChildren+0xe4
04ac6f48 67ad9d15 1188efb8 04acae4c 04ac6f74 MSHTML!CDispContainer::DrawContentAdvanced+0x25c
04ac718c 67ac78e9 04acae4c 00000000 00000007 MSHTML!CDispContainer::DrawSelf+0x49b
04ac72a4 67adcb19 1188efb8 00000000 00000007 MSHTML!CDispNode::Draw+0x2c8
04ac72c8 67a82102 0ec815ec 1188efb8 04ac73c8 MSHTML!CDispNode::DrawContainerChild+0xd4
04ac7340 67a80e8b 0ec815a0 00000001 00000003 MSHTML!HtmlLayout::LineBoxBuilder::LsInlineBlockDisplay+0x71
04ac7358 67a801ca 1682e1f0 04ac73a0 0ec815a0 MSHTML!Ptls5::LsUpdateBreakRecordText+0xe4
04ac73d0 67adc0f8 112b8118 04ac7454 00000000 MSHTML!Ptls5::LsDisplayLine+0x19b
04ac747c 67adbf96 11576038 16a20920 1188ef60 MSHTML!HtmlLayout::LineBox::Draw+0x127
04ac75fc 67adbcb6 16a20920 04ac7698 00000000 MSHTML!HtmlLayout::FlowBox::DrawFlowItems+0x417
04ac76d0 67adba8d 04ac77b4 04ac77c4 04acd308 MSHTML!HtmlLayout::FlowBox::DrawClientContainerContent+0x1f7


eax=00000000 ebx=0767dc08 ecx=00000000 edx=67b36c31 esi=00000001 edi=0767dc08
eip=67b50f2c esp=043fd05c ebp=043fd070 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
MSHTML!CElement::AddRefCDocument:
67b50f2c 8b8130010000    mov     eax,dword ptr [ecx+130h] ds:002b:00000130=????????
0:005> kb
ChildEBP RetAddr  Args to Child              
043fd058 67b7ce5d 00000001 0767dc08 0972de00 MSHTML!CElement::AddRefCDocument
043fd070 67b7faa9 0767dc08 0e90b258 0e5f5d00 MSHTML!CElement::PrivateExitTree+0xfa
043fd0dc 67b7d897 0e90b258 00000001 00000001 MSHTML!CMarkup::DestroySplayTree+0x1fd
043fd150 67b87d82 00000000 00000001 0e5f5c20 MSHTML!CMarkup::UnloadContents+0x4bb
043fd170 67b94f28 0e90b258 00000001 00000000 MSHTML!CMarkup::TearDownMarkupHelper+0x4c
043fd198 67b96c2f 00000001 00000000 00000000 MSHTML!CMarkup::TearDownMarkup+0x71
043fd1c8 67b97190 0e5f5c20 00000000 0e5f5c20 MSHTML!CDoc::UnloadContents+0x5ee
043fd1e4 67b2adb8 0af3d9f8 0af3d9f8 043fd21c MSHTML!CDoc::Passivate+0x158
043fd1f4 67b59914 0e5f5c20 6783edfd 00000000 MSHTML!CBase::PrivateRelease+0x33
043fd1fc 6783edfd 00000000 0af3d9f8 00000000 MSHTML!TSmartPointer<IDispBrush>::Release+0x14
043fd20c 67b2adb8 008f8d10 00000000 043fd230 MSHTML!COleSite::Passivate+0x9f
043fd21c 67b2ac22 0af3d9f8 008f8d10 008ff680 MSHTML!CBase::PrivateRelease+0x33
043fd230 67b2acfe 0af3d9f8 043fd2b0 67bc5bdb MSHTML!CElement::PrivateRelease+0x40
043fd23c 67bc5bdb 0af3d9f8 008fecc8 67adfe91 MSHTML!CXDomainRequest::Release+0x10
043fd248 67adfe91 0e5ff8d0 008bc5a8 02000000 MSHTML!CMultimediaLog::Reset+0x28
043fd2b0 67ae017b 0e5ff8d0 00000000 00000001 MSHTML!COmWindowProxy::SwitchMarkup+0xadf
043fd338 679f4971 0e5ff8d0 00000000 0e5ff8d0 MSHTML!CMarkup::SetInteractiveInternal+0x183
043fd36c 67a04c0f 00000001 00000000 0e5ff8d0 MSHTML!CMarkup::RequestReadystateInteractive+0x152
043fd398 679eeaa7 008dd2c0 0aebcf70 0556e892 MSHTML!CMarkup::BlockScriptExecutionHelper+0x184
043fd4a4 679b7cf8 0556e892 008dd2c0 0aebcf70 MSHTML!CHtmPost::Exec+0x4b1


eax=00000000 ebx=00000000 ecx=07f4b8d4 edx=04b695fc esi=08564494 edi=00000000
eip=6789db2b esp=04b695ac ebp=04b695dc iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
MSHTML!CDXImageSoftware::CreateSharedBitmap+0xb4:
6789db2b 8b08            mov     ecx,dword ptr [eax]  ds:002b:00000000=????????
0:004> kb
ChildEBP RetAddr  Args to Child              
04b695dc 6789d969 00000000 081efac4 04b695fc MSHTML!CDXImageSoftware::CreateSharedBitmap+0xb4
04b69610 67ace39f 745b8fc0 081efac4 00000000 MSHTML!CDXImageSoftware::PrepareToRenderImpl+0x82
04b69644 67e7c992 08564480 745b8fc0 00000000 MSHTML!CDXImage::PrepareToRender+0x7c
04b696e0 67c0c4cf 745b8fc0 04b69878 04b6973c MSHTML!CSATBlurRenderer::CreateBlurredTarget+0x33b
04b69774 677faef8 745b8fc0 04b69954 40000000 MSHTML!CDXRenderTarget::Gaussian+0x1bf
04b699b8 677fa4d6 04b69a9c 04b69bd0 00000000 MSHTML!CDispSurface::RenderShadows+0xcc0
04b69cac 67adb82f 6a5a4220 04b69d28 081fbca0 MSHTML!HtmlLayout::ContainerBox::PositionAndDrawBackground+0x1170
04b69d58 67adb6b2 081f1d18 04b6d8d8 081fbca0 MSHTML!HtmlLayout::ContainerBox::DrawBackgrounds+0x210
04b69d88 67aca06f 04b69f6c 04b69e7c 04b6d978 MSHTML!HtmlLayout::ContainerBox::DrawClientBackground+0x6f
04b69e94 67ac9f07 04b6b4bc 00000000 04b6b4bc MSHTML!CDispNode::DrawBackground+0x1c0
04b6a0cc 67ac78e9 04b6b4bc 00000000 00000007 MSHTML!CDispContainer::DrawSelf+0x2b0
04b6a1e4 67ac82d0 081fbca0 00000000 00000007 MSHTML!CDispNode::Draw+0x2c8
04b6a208 67ad9c5e 00000000 04b6a2d4 04b6b4bc MSHTML!CDispContainer::DrawChildren+0xe4
04b6a2a8 67ad9d15 07c69e18 04b6b4bc 04b6a2d4 MSHTML!CDispContainer::DrawContentAdvanced+0x25c
04b6a4ec 67ac78e9 04b6b4bc 00000000 00000007 MSHTML!CDispContainer::DrawSelf+0x49b
04b6a604 678d0958 07c69e18 00000000 00000007 MSHTML!CDispNode::Draw+0x2c8
04b6a64c 67ac78e9 04b6b4bc 00000000 00000007 MSHTML!CDispProxyNode::DrawSelf+0x11d
04b6a764 67ac82d0 0849b398 00000000 00000007 MSHTML!CDispNode::Draw+0x2c8
04b6a788 678d0896 04b6d978 04b6a854 04b6b4bc MSHTML!CDispContainer::DrawChildren+0xe4
04b6a828 67ad9d15 745b8da8 04b6b4bc 04b6a854 MSHTML!CDispContainer::DrawContentAdvanced+0x28d


eax=0684a9a0 ebx=00000000 ecx=727937c0 edx=0000025b esi=73de91e8 edi=00000001
eip=6682bbbb esp=0440d190 ebp=0440d1a0 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
jscript9!Js::JavascriptOperators::OP_GetInstanceScoped+0x2a:
6682bbbb 8b4304          mov     eax,dword ptr [ebx+4] ds:002b:00000004=????????
0:005> kb
ChildEBP RetAddr  Args to Child              
0440d1a0 09ed0071 00000003 0000025b 5ce84740 jscript9!Js::JavascriptOperators::OP_GetInstanceScoped+0x2a
WARNING: Frame IP not in any known module. Following frames may be wrong.
0440d1c8 668085fe 725d5980 00000002 02f5bcf0 0x9ed0071
0440d204 66808523 725d5980 6680cb68 00000002 jscript9!Js::JavascriptFunction::CallFunction+0xc4
0440d268 6680845a 0a244dc8 00000002 0440d360 jscript9!Js::JavascriptFunction::CallRootFunction+0xb6
0440d2a4 668083e6 00000000 0440d2d4 00000002 jscript9!ScriptSite::CallRootFunction+0x4f
0440d2cc 6687e0d7 725d5980 0440d2fc 00000000 jscript9!ScriptSite::Execute+0x63
0440d330 67a824a9 0a23606c 725d5980 00000002 jscript9!ScriptEngine::Execute+0x11a
0440d3b4 67a823d3 725d5980 094bfae8 63beed58 MSHTML!CListenerDispatch::InvokeVar+0x12a
0440d3d4 678ead1f 094bfae8 0440d438 0440d498 MSHTML!CListenerDispatch::Invoke+0x40
0440d5a4 67b6bbfc 094bfae8 728f5120 00000000 MSHTML!CEventMgr::Dispatch+0x537
0440d5cc 68073e2c 728f5120 7ec86aa0 ffffffff MSHTML!CEventMgr::DispatchEvent+0xc9
0440d5e0 68079c98 6a40df58 adc49da3 00000096 MSHTML!CSVGElement::Fire_SVGLoad+0x37
0440d5f8 680760bd 0440d620 67b77389 728f5120 MSHTML!CSVGSVGElement::Fire_SVGLoad+0x53
0440d600 67b77389 728f5120 00000000 00008003 MSHTML!CSVGElement::Fire_SVGLoad_Async_Handler+0x10
0440d620 67b77406 748dbf58 00000001 90fa7997 MSHTML!CAsyncEventQueue::DispatchAllEvents+0x7c
0440d670 74fb62fa 00700e80 00000aae 748dbf58 MSHTML!GlobalWndProc+0x2ed
0440d69c 74fb6d3a 67b145ee 00700e80 00008003 USER32!InternalCallWinProc+0x23
0440d714 74fb77c4 00000000 67b145ee 00700e80 USER32!UserCallWinProcCheckWow+0x109
0440d774 74fb788a 67b145ee 00000000 0440f8a8 USER32!DispatchMessageWorker+0x3bc
0440d784 6b8a205c 0440d7cc 001f6db0 001f6dcc USER32!DispatchMessageW+0xf


eax=04666084 ebx=0bd43ae8 ecx=04666060 edx=0466067f esi=00000000 edi=0bd43b48
eip=6908145b esp=04666078 ebp=046661ec iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
MSHTML!CSVGEmbeddedComponent::CreateDisplayTreeForEmbeddedContent+0x79:
6908145b 8b06            mov     eax,dword ptr [esi]  ds:002b:00000000=????????
0:005> kb
ChildEBP RetAddr  Args to Child              
046661ec 69081acc 0bd43ae8 0bd43ae8 0bd43aec MSHTML!CSVGEmbeddedComponent::CreateDisplayTreeForEmbeddedContent+0x79
04666208 68e5dda5 01d43ae8 0c2c0068 00000001 MSHTML!CSVGEmbeddedComponent::EnsureLayoutForEmbeddedComponent+0xf7
04666260 690b59c3 0b010600 04669bd8 046662b4 MSHTML!CImgHelper::DrawSVGImage+0x29f
046662d8 68ffe222 04669bd8 0466630c 12d406e8 MSHTML!CSVGImageBlock::Draw+0x1a8
0466631c 68ad6f4e 04666354 6901ae94 046663dc MSHTML!HtmlLayout::SvgPrimitiveBox::DrawClient+0x117
04666380 770f26a4 00000000 04669bd8 046677bc MSHTML!CDispDrawContext::GetRedrawRegionBounds+0x7b
04666498 770f256f 0edcc0a8 00000090 04669ce4 ntdll!RtlpReAllocateHeap+0x190
0466650c 68ad6f4e 00570000 00000000 0edcc0a8 ntdll!RtlReAllocateHeap+0x2c5
04666548 68ad8db1 046677bc 04669ce4 00000000 MSHTML!CDispDrawContext::GetRedrawRegionBounds+0x7b
046677bc 00000000 00000000 00000000 04667804 MSHTML!CDispSurface::CClipStack::PushClipRect+0x181


eax=00000024 ebx=00005867 ecx=00000009 edx=00000016 esi=0435d008 edi=00000000
eip=68ffaa2c esp=0435cf98 ebp=0435d18c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
MSHTML!HtmlLayout::SvgTextBoxBuilder::CalculateGlyphWidths+0xaf:
68ffaa2c f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:005> kb
ChildEBP RetAddr  Args to Child              
0435d18c 68ffa7cc 08fab420 0435d1d0 61b9c288 MSHTML!HtmlLayout::SvgTextBoxBuilder::CalculateGlyphWidths+0xaf
0435d1d8 68ff8684 00fab420 6adadd40 0435d478 MSHTML!HtmlLayout::SvgTextBoxBuilder::UpdateVisibleRectangle+0x7f
0435d280 68ff8150 08fab420 0435d478 08fab420 MSHTML!HtmlLayout::SvgTextBoxBuilder::BuildLine+0x4d2
0435d2a8 68a36a8d 0d01ff24 0435d478 0d01ff18 MSHTML!HtmlLayout::SvgTextBoxBuilder::MoveToNextPosition+0xb5
0435d2d4 68a33951 08fab420 0435d478 0d01ff18 MSHTML!HtmlLayout::LayoutBuilder::EnterBlock+0xca
0435d2f0 68a359e4 0435d420 0435d464 00000000 MSHTML!HtmlLayout::LayoutBuilder::Move+0x48
0435d3c0 68a319af 0ad93c90 00000000 0ad93c90 MSHTML!HtmlLayout::LayoutBuilderDriver::StartPartialLayout+0x2d1
0435d4a0 68a2ffcc 0c10ddb8 00000000 00000032 MSHTML!HtmlLayout::CIE9Page::LayoutPage+0x27c
0435d4cc 68a2c3a7 00000000 0c10ddb8 08fa0c90 MSHTML!HtmlLayout::CIE9DocumentLayout::FormatPage+0x65
0435d524 68a272fe 0c8dff24 00000000 08fa0c90 MSHTML!CCssDocumentLayout::FindOrFormatPage+0x272
0435d590 68a2deff 0ad93c90 0c8dff24 00000032 MSHTML!CCssDocumentLayout::GetPage+0x95b
0435d638 68a2dd12 0c8dff20 0435d654 0435d6c8 MSHTML!CMarkupPageLayout::CalcSize+0x28c
0435d6b0 68a2fa31 00100000 0435d6c8 0c8dff34 MSHTML!CMarkupPageLayout::CalcTopLayoutSize+0x101
0435d6d4 68c9b1d1 00100000 00002000 00000000 MSHTML!CMarkupPageLayout::DoLayout+0x56
0435d710 68a16b35 0c602e1c 00100000 00000000 MSHTML!CView::ExecuteLayoutTasks+0x3b
0435d77c 68a3856b 00000000 004d4f80 004d4fb4 MSHTML!CView::EnsureView+0x3bf
0435d7a4 68b49ef9 0c602e1c 00000000 00000000 MSHTML!CView::EnsureViewCallback+0xb8
0435d7e0 68b69768 3286b286 00000000 68b245ee MSHTML!GlobalWndOnMethodCall+0x115
0435d828 764e62fa 000909b6 0000005b 00000000 MSHTML!GlobalWndProc+0x302
0435d854 764e6d3a 68b245ee 000909b6 00008002 USER32!InternalCallWinProc+0x23


eax=00000001 ebx=00000000 ecx=00000000 edx=765d4758 esi=00000000 edi=00000000
eip=7675561c esp=043dcb38 ebp=043dcb40 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
SHLWAPI!StrCmpNICW+0x13:
7675561c 0fb706          movzx   eax,word ptr [esi]       ds:002b:00000000=????
0:005> kb
ChildEBP RetAddr  Args to Child              
043dcb40 765d5704 00000000 765d4758 00000004 SHLWAPI!StrCmpNICW+0x13
043dcb60 765fcf6f 043dd000 72865284 00000000 urlmon!IsKnownProtocol+0x1e
043dcba4 68903047 007e81e8 00000000 00000000 urlmon!COInetSession::CreateBinding+0xf2
043dcbd8 68902f8f 72865284 00000000 00000000 MSHTML!CTridentFilterHost::CreateInetProtBinding+0x45
043dcc04 689c6406 00000000 00000000 043dd000 MSHTML!CTridentFilterHost::BindToInetProt+0x112
043dce98 689c18ba 72865218 00000000 00000004 MSHTML!CDwnBindData::Bind+0x4ba
043dcec0 689c16f6 00000000 68756ef4 00000000 MSHTML!NewDwnBindData+0x19d
043dcf1c 68a0a3bd 68756ee0 043dd000 6db14b88 MSHTML!CDwnLoad::Init+0x25c
043dcf40 68b62217 043dd000 6db14b88 00000001 MSHTML!CImgLoad::Init+0x43
043dcf68 68b6211d 07083df0 00000000 043dd000 MSHTML!CDwnInfo::SetLoad+0x11e
043dcf88 68b6c334 00000001 043dd000 00000000 MSHTML!CDwnCtx::SetLoad+0x86
043dcfb4 689f1547 00000001 043dd000 00000000 MSHTML!CImgCtx::SetLoad+0x4d
043dd058 689cc92d 00000001 0a4a138c 08df93b0 MSHTML!CDoc::NewDwnCtx2+0x337
043dd088 68ab962d 6fcd52a0 00000001 08df93b0 MSHTML!CDoc::NewDwnCtx+0x5b
043dd0d4 68aba4c3 07083df0 679f53e8 00000004 MSHTML!CImgHelper::FetchAndSetImgCtx+0xfb
043dd0f8 68aba3df 043dd1b8 08df93b0 043dd184 MSHTML!CImgHelper::EnterTree+0x132
043dd16c 6908fd6c 06f03f20 043dd1b8 043dd1b8 MSHTML!CImgHelper::Notify+0x2a4
043dd18c 689c2fa1 043dd1b8 00000001 6f48ea00 MSHTML!CSVGImageElement::Notify+0x2c
043dd1ec 689c2e5f 6f48ea00 6db102d8 689c7aa7 MSHTML!CHtmRootParseCtx::FlushNotifications+0x1b6
043dd1f8 689c7aa7 00000000 74702280 74702280 MSHTML!CHtmRootParseCtx::Commit+0xb

January 27, 2013

Entering into Out of Memory Condition

In this blog post I'm describing an approach to force the execution flow to enter into out of memory (OOM) error conditions when the amount of memory to allocate is not controlled by the attacker as in the example below.
#define MAX_SIZE 0x08000000 /* 128M */

ptr = malloc(MAX_SIZE)
if (ptr == NULL)
{
  return OUT_OF_MEMORY;
}
When testing software, it's reasonable to think that error conditions like above are unlikely to be reached as often as other error conditions. Some of them may never get reached because it's not obvious to make the allocation with fixed-size to fail.

OOM error condition could be a potential to enter into vulnerable code path. One example I've heard that the memory allocation had failed and the error condition led to call the cleanup functionality which caused to free of uninitialized pointer.

When testing, one possible way to make memory allocation to fail is to hook the memory allocation function and change the functionality to fail, that is to return with NULL pointer. However, I wanted to avoid this approach of testing because even if I find bug I'd still need a lot of work to reproduce it in real word scenario (if possible at all), when the hook is not applied.

Therefore, what I thought is that the other way to force fixed-size memory allocation to fail is to consume large amount of memory of the virtual address space, and as a consequence the program will be operating under low-memory conditions. This means if memory allocation occurs with reasonable small size it could fail because there is not much memory available. My theory is that any vulnerability found that we reach via OOM error conditions can be reproduced in a real word scenario because we always expect to find something that triggers to consume large amount memory in order to make other allocation to fail. This is pretty much possible in browsers where basically you can easily fill arbitrary amount of the heap memory.

Experimentally, I involved this approach in my fuzzing methodology. I attach the target process to Windbg and execute .dvalloc command to allocate arbitrary amount of memory in the virtual address space. The amount of memory available in the address space can be queried with !address -summary command, or can be seen in Process Explorer, and so you can allocate more for your test if needed.

When testing Firefox with this methodology I notice increased amount of int 3 crashes in mozalloc_abort() that is due to out of memory condition - those crashes are not exploitable though.
  This blog is written and maintained by Attila Suszter. Read in Feed Reader.