Win32 Stack Based Buffer Overflow Walkthrough sk@scan-associates.net 23rd July 2002 1.0 Introduction 2.0 What is buffer overflow? 2.1 Stack overflow 3.0 What do you need? 4.0 Exploiting stack based buffer overflow 4.1 But where can we jump to? 4.2 Finding jmp esp 5.0 The payload 5.1 Getting Windows API/function absolute address 6.0 Ownership 7.0 Greets and thanks 8.0 The code 9.0 Where to get more info 10.0 Patch 1.0 Introduction There are already many good articles published about buffer overflow. However we think there is still room for Windows based buffer overflow article. We wrote this article because we would like to document the whole process from discovering a bug till successful penetration of a server in one of our recent penetration testing. You will see many practical examples and useful code that may help you to discover and exploit other buffer overflow. We hope that it will be useful but please check out the last part "9.0 Where can I get more info?" for links to papers that we learnt much from. 2.0 What is buffer overflow? Computer programs usually allocate certain amount of space to store data during execution. This space is known as buffer. A buffer overflow occurs when the amount of data is larger than the allocated buffer. When that happened, the data will overwrite memory area that followed the buffer. There is no telling what is after the buffer; however what we hope to overwrite is memory area which will alter the execution flow of the program. The goal is to direct the execution flow to our code, thus allow us to execute anything in the victim PC. 2.1 Stack overflow Function calls in C program usually pass parameter via stack. A caller program will store parameters into stack before calling a function. The function will then locate the parameters from the stack. Stack also will contain return address so that the function can jump back to the caller program. If we can submit data more than previously allocated space, we can overflow the dedicated space and if we can overwrite the stack, we call this Stack Based Overflow. Overflow the stack is especially fun because stack usually contain return address. For more information about it, you may want to look at a classic article from Aleph One (http://www.phrack.org/show.php?p=49&a=14). 3.0 What do you need? Windbg.exe (http://www.microsoft.com/ddk/debugging/default.asp) TASM (http://community.borland.com/museum) Hex Editor (I prefer xvi32 http://www.chmaas.handshake.de/delphi/freeware/xvi32/xvi32.htm) Visual Studio or any decent C compiler Microsoft SQL Server 7.0 or SQL Server 2000 Windows 2000 Server SP2. 4.0 Exploiting stack based buffer overflow We are going to go through the process of exploiting the latest version (as the time we are writing this) of Microsoft SQL Server 7.0 Service Pack 4 with default installation. Soon after Mark Litchfield published a buffer overflow in OpenDataSource() with Jet database engine in SQL Server 2000 (http://www.nextgenss.com/advisories/mssql-ods.txt), we browse through the list of default install ODBC driver. We found the FoxPro driver install by default, and decided to try our luck with OpenDataSource(). We fire up WinDbg.exe. Click File, and then Attach to Process. You need to debug SQL Server. Select ???sqlservr.exe??? And run (F5) process. Now, whenever there is Exception, User break, etc, the debugger will suspend the process. We need to do this because we want to see what we can overwrite and use. Now, open up your Query Analyzer. Try executing this query (yeah, type about 300 A???s). SELECT * FROM OpenDataSource( 'MSDASQL','Driver=Microsoft Visual FoxPro Driver;SourceDB=e:\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAA;SourceType=DBC')...xactions; Here, we have prepared it for you. You may just cut and parse it to your Query Analyzer. Run the query. The query should overflow the SourceDB parameter, and it will overwrite several CPU registers as well as the ever important EIP. Before Query Analyzer can return any result, your WinDbg already kick in. If you check your WinDbg now, you should see that the instruction is pointing at 0x41414141, which is an invalid address. Take a look at register EIP, it is 0x41414141. We have overwritten EIP with the ASCII code of ???A??? (0x41). The process flow to 0x41414141 because we have overwritten the EIP register. This register determines the next instruction that the CPU will execute. Being able to write to EIP means we can control the execution flow. That mean somewhere in the 300 A???s will overwrite the EIP register. We need to find the exact location so that we can inject useful address to EIP. To get the exact location of the EIP, we can construct a query like the following: SELECT * FROM OpenDataSource( 'MSDASQL','Driver=Microsoft Visual FoxPro Driver;SourceDB=e:\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAZZZZYYYYXXXXWWWWVVVVUUUUTTTTSSSSRRRRQQQ QPPPPOOOONNNNMMMMLLLLKKKKJJJJIIIIHHHHGGGGFFFFEEEEDDDDCCC CBBBBAAAA;SourceType=DBC')...xactions; You may need to terminate your SQL server, attach to process again using WinDbg. Run Query Analyzer and connect to your SQL server again. This time, your EIP will be 0x47484848. This is equivalent to GHHH. We need to replace GHHH with a useful memory address, may be memory address that point to our payload. The payload will execute anything we want. It also tells us that we need to put our memory address in reverse byte sequence. Let???s construct a query that just enough for us to overwrite EIP. It will take 269 A???s for padding and 4 more bytes that will overwrite the EIP. SELECT * FROM OpenDataSource( 'MSDASQL','Driver=Microsoft Visual FoxPro Driver;SourceDB=e:\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB;SourceType=DB C')...xactions; Now, take a look at WinDbg. Access Violation is trying to execute code from 0x42424242. ASCII code of B is equivalent to 0x42, which is the last part of the SourceDB string. The process flow to 0x42424242 because ???BBBB??? have overwritten the EIP register. By replacing BBBB with a memory address, the process will flow into that memory address. In other word, we can jump to anywhere we want. 4.1 But where can we jump to? Of cause, we are going to jump to our payload. Our payload will execute something useful like spawning a shell for us, creating a file and so on. It is possible to jump directly to our payload if we know the address of our payload. To do that, we just need to replace BBBB with our address. But usually, the address of our payload may not be in a fix location/address all the time. It is best if we can find a register that point to our buffer or query in this case. We can then jump to the address store in the register to get to our buffer. This method is preferred because we can jump to our code/buffer no matter where it is. Let???s find the register. Take a look at what each register hold during the crash: EDI=0 ESI=EB2288 EBX=FFFFFFFF EDX=301FCB10 ECX=301FCAC0 EAX=AB EBP=41414141 EIP=42424242 ESP=301FCC50 We need to find a register that is related to our buffer. If you type the value of EDX to the Memory window inside WinDbg, you will see that it points to a location above the long e:\AAAA???AAAA buffer. If we want to jump to EDX, we must be able to put our payload before the e:\AAAA buffer, which is not possible. Let???s take a look at ESP. It points to memory location just after the BBBB. This is perfect. If we jump to the value hold by ESP, we will jump back to our buffer. We will land on the byte immediately after the value we overwrite EIP. So the structure of our query should look like this: SELECT * FROM OpenDataSource( 'MSDASQL','Driver=Microsoft Visual FoxPro Driver;SourceDB=e:\A???A<EIP><payload>;SourceType=DBC')...xactions; Now that we have found a perfect location for our payload, all we need to do is to jump to that location. In order to do that, we need to execute something like ???jmp esp???. We can overwrite the EIP to point to somewhere in the memory that contain instruction ???jmp esp???. When the CPU reaches memory address that contains the instruction, it will jump back to our payload because ESP point to our payload. We are free to do whatever evil thing we want. 4.2 Finding jmp esp As mentioned, we need to overwrite EIP with an address that contain instruction ???jmp esp???. First, let???s find out what this instruction is, in machine code or opcode. You may use the handy debug.exe that comes with every single version of Windows. Run debug.exe, you will see a dash (-) indicating debug is now expecting your command. You can type question mark (?) for help to list of available command. We can use debug to type assembly code ???jmp esp??? and dump the memory to see the actual machine code of the instructions. To get the instruction code for ???jmp esp??? follow these ins...
swepper