Read the article "WindowsNT Buffer Overflow's From Start to Finish"

WindowsNT Buffer Overflow's From Start to Finish

I've read most of the articles on BO's(Buffer Overflows) on the net. I have found that they either for *NIX systems, or they are not detailed enough. The author's usually take some known vulnerable software and show you step by step how to exploit it. I am going to take a different approach. I am going to write an app that has a buffer overflow when reading data from a file. Then I will write an app that will create the file, that when read, will cause the exploit. I will also include an opcode finding tool.

Tools Needed:
Visual C++ 6.0
Windows NT

*The code and addresses I use are for Windows NT Workstation 4.0 SP6 .First lets write the app that will contain the bufferoverflow. We also want the app to be able to read in some type of file so we can actually exploit this from some typeof script. So in Visual C++ create a new console application,select "An Application that supports MFC" and click Finish.This does not necessarily have to be a MFC app, but I prefer to use some of the MFC classes. Obviously, I am a windows programmer. So let's add some exploitable code here. This is what it will look like:

CWinApp theApp;

using namespace std;

void overflow(char* buff);

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;
}

void overflow(char* buff)
{
	CFile file;
	CFileException er;
	if(!file.Open(_T("overflow.txt"),CFile::modeRead,&er))
	{
		er.ReportError();
		return;
	}

	int x = file.GetLength();
	file.Read(buff,x);
}

Let's analyze the code a bit now and find where the problem actually is. Since this is an MFC console app, the "main" routine may look a little different, but it works the same. Let's skip to the else section inside main. You see the first line, char buff[10]. We have allocated a local variable, buff which is an array of 10 chars. We all know local variables are allocated on the stack right? So now we call the function overflow and pass it our buff. Now lets look inside the overflow function. First we instantiate a CFile object, then a CFileException object. Now we will attempt to open a file named "overflow.txt" from the current directory, with read access. If we open the file successfully we will get the files length, then we will read the entire contents of the file into our buff. Do you see the problem here? buff is only 10 chars. What happens if the file we read is 100? BUFFER OVERFLOW. But, the big problem is that we are overflowing a buffer which exists on the stack. When we can write to the stack we can do some strange things. As you will soon see. So now lets create a text file called overflow.txt and place it into the project directory of the first application.
Let's step to the side for a second, a little explanation of WindowsNT memory architecture is in order here. In NT every process (executable) is given 4GB (0xFFFFFFFF) of virtual memory when it is started. Some of this memory is actually shared among all processes, like kernel and device driver areas. But those areas are mapped to each processes virtual address space. No process actually gets 4GB of phyiscal memory, only the memory necessary is actually allocated from physical. So every process has full 4GB of virtual memory, which ranges from 0x00000000 to 0xFFFFFFFF. These areas are divided. 0x00000000 to 0x0000FFFF is reserved for NULL pointer assignments. Attempting to access memory in this area will cause an access violation. 0x00010000 to 0x7FFEFFFF is the processes user space. This is where the exe image is loaded (starting at 0x00400000) and DLL's are loaded. If code (a DLL or EXE) is loaded at a certain address in this range it can be executed. Accessing an address which does not have code loaded in it will cause an access violation. 0x7FFF0000 to 0x7FFFFFFF is reserved bad pointer assignments and you will get an access violation with any attempt to access it. 0x80000000 to 0xFFFFFFFF is for operating system use only. Things like Device Drivers and other Kernel level code is stored here. Attempting to access this area from a user level application (ring 3) will cause an access violation.
Now back to the overflow.txt file. We are going to keep putting characters into our text file until we see the dialog popup informing us of an application error and what memory we attempted to access. Which character you chose to fill this text file with is important, as you will see in minute. Let's start by filling the text file with a's. Lower case a's. We know the buffer will hold ten so lets start with 11(make sure your application being built in debug mode or your results will be different). 11 doesn't work so we keep increasing it. 18 finally causes a crash. This crash isn't anything special yet. We've just totally screwed up the stack and it shows. Lets add six more a's, for a total of 24. Run the program and watch the dialog popup explaining to us that instruction at 0x61616161 had referenced memory at 0x61616161. You do know that the hex value for the ascii character a is 0x61 right? If you have Visual C++ installed you will be able to hit cancel now, and it will debug the application. Once visual studio is open, open you registers window. To do that go to the view menu, then debug window, and select registers. If you don't know anything about assembly, you should, get a book and READ IT. We see that EAX has been taken, and so has EBP and EIP. The most important thing is EIP. By being able to fill in the EIP with whatever we want we are able to jump to any code in memory. And what makes this even easier is that our ESP is not destroyed. It seems to point near the area on the stack that we control. We need to test this to find out.
Now let's get into this. Set a breakpoint on the last bracket of the main routine, we only care about what happens here. Now start the debugger and it will make it to this breakpoint with no errors. Now we need to switch into disassembly view. If you have the standard keyboard setup for Visual C++ press alt+8, if not go to the view menu, debug windows, and select disassembly Also open your memory and registers windows if you haven't already. You should see something similiar to this:

004011DB 5F                       pop         edi
004011DC 5E                       pop         esi
004011DD 5B                       pop         ebx
004011DE 83 C4 50             add         esp,50h
004011E1 3B EC                  cmp         ebp,esp
004011E3 E8 28 04 00 00    call        _chkesp (00401610)
004011E8 8B E5                   mov         esp,ebp
004011EA 5D                      pop         ebp
004011EB C3                       ret

So what is that junk? It's assembly code. You do know assembly right? Even if you don't, I'll try to make this easy to understand. Starting at the top we have pop edi. The pop instruction will remove one item from the top of the stack and place it into whatever register. In this caseedi. Also important here is the ESP. The ESP is the 32 bit stack pointer. A pop will mov(e) the top element from the stack, in this case a DWORD (4 bytes), put it in whatever register, and increment the stack pointer by 4 (because of the 4 bytes). So before making another step, look at ESP. In the memory window enter ESP. You will now see exactly where esp is pointing to and what is there. Look at the four bytes pointed to by ESP and watch edi. Now step over this instruction and notice that edi is now filled with whatever esp pointed to, and esp has been incremented by four. Now the next two instructions are the same, but different registers, step over them and see that they work the same way. The next three lines are not very important to us. To understand them you will need to follow the assembly from the beginning of the routine, and we aren't doing that. Just step over them, they do nothing special. Now onto the line, mov esp,ebp. You read this line, right to left. This will mov(e) whatever is in EBP into ESP. This also does nothing special for us. Now onto pop ebp. 
Here is where this gets interesting. Remember what a pop 

does, it removes the top element from the stack. Now lets take a look at where we our ESP is pointing to, cause whatever four bytes are there are about to go into EBP. So again type esp into your memory window. We have a bunch of 0x61's there (hex value of 'a'). So 0x61616161 is about to be popped into ebp. Step over the instruction and verify that it does. Sure enough, that is what happens. But that doesn't really get us anywhere. Now the next line, ret. Ret is the assembly return instruction. But there is more to it than just returning. How does it know where to return to? By the address that is supposed to be sitting on the stack right now. The return would be the equivalent of pop eip (which you can't do). It takes the four bytes that ESP points to and moves them into EIP. And EIP is our 32 bit instruction pointer. This mean, whatever address EIP points to, is the next instruction to get executed. So once again, type esp into the memory window and see what we are about to put into EIP. Well what do you know, another four bytes of 0x61. So step over the ret instruction and watch what happens. EIP will become 0x61616161 and you will be about to execute the instruction at 0x61616161. Which in my case is nothing ???, invalid memory. So step over again and you get an access violation. Now look at ESP. It correctly points to the next area on the stack. For some reason, if you run the program independant of the debugger and let it crash so you get the ok/cancel dialog, and then press cancel. When you land on 0x61616161 your ESP will be wrong. I'm not sure why that is, but it works as expected when you step through it line by line like we just did. So now we got the program to execute, or attempt to execute code at 0x61616161, which means we can take over the EIP. So lets see if we can overflow the stack some more, so that when we get to 0x61616161 our ESP points to the rest of our overflow. So lets add another 4 a's to our text file and debug again. We now have 28 a's in our text file. So we view the disassembly again, make sure to have your memory window and register windows open. Step through and over the ret instruction. You are now at 0x61616161 again. Now type esp into the memory window and look what is there. Just as we suspected, there are 4 0x61's there. Now we are in business.

Let me go back to a point I made earlier. We used a's (0x61) to fill our text file to determine if there was an overflow. So since EIP became 0x61616161 we attempted to access instructions at that address. In my case there was invalid memory there so it was an access violation. But what if there had been code there? Maybe a DLL loaded or something. Well, it would have executed that code and probably done something totally different. The same thing could have happened if we would have used, A's instead of a's. A's hex value is 0x41. So we would have jumped to 0x41414141 instead of 0x61616161. There could be code there and it would have executed it. So keep those things in mind.

So we can control the EIP, the ESP points to the rest of the stack, and we can fill the stack with whatever we like. So now what? Would it be nice if we could could just jump to ESP and start executing? Well we can, hopefully. Jmp ESP is in fact a legal instruction. This instruction would mov(e) whatever is in ESP into EIP and begin executing instructions there. So we need to somehow call jmp esp. Hmm, how can we do that? Well, lets think. We do have control of EIP, so we can jump to where ever we want in our process space. If we can fill EIP with the address of a jmp esp instruction somewhere in memory we are in business. So how do we find out if there is a jmp esp instruction somewhere in our process space? It's easier than you think. The first thing we need to do is figure out what the opcodes for jmp esp are. The opcodes are the machine instructions that programs are compiled into so they can be executed. So let's create a new app in Visual C++. Again a console app, and again with MFC. Enter the following code:

CWinApp theApp;

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;
}

Now set a breakpoint on the return 0; statement, because the inline assembly line will not get executed. Start the debugger and let it run to the breakpoint. Now open up the disassembly debug window. Right click on the window to turn on source annotation and code bytes. Now look at the line which contains jmp esp. To the left of jmp esp and to the right of its address, you will see its code bytes or opcodes. The opcodes for jmp esp are FF E4. So now that we know that, how do we find that in oour process space? Let's add a bit more code to this app. Change it to the following:

CWinApp theApp;

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("Kernel32");

		h = GetModuleHandle(dllname);
		if(h == NULL)
		{
			h = LoadLibrary(dllname);
			if(h == NULL)
			{
				cout<<"ERROR LOADING DLL: "<
时间: 2024-09-28 01:53:28

Read the article "WindowsNT Buffer Overflow&#39;s From Start to Finish"的相关文章

Read the article &quot;WindowsNT Buffer Overflow&#039;s From Start to Finish&quot;

WindowsNT Buffer Overflow's From Start to Finish I've read most of the articles on BO's(Buffer Overflows) on the net. I have found that they either for *NIX systems, or they are not detailed enough. The author's usually take some known vulnerable sof

CVE-2016-10190 FFmpeg Http协议 heap buffer overflow漏洞分析及利用

1. 背景 FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器.转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具.2016年末paulcher发现FFmpeg三个堆溢出漏洞分别为CVE-2016-10190.CVE-2016-10191以及CVE-2016-10192.本文对CVE-2016-10190进行了详细的分析,是一个学习如何利用堆溢出达到任意代码执行的一个非常不错的案例. 2. 漏洞分析 FFmpeg的 Http 协议的实现中支持几种不同的数据传输方式,通

oracle-1ORU-10027:buffer overflow limit of 2000 bytes;

问题描述 1ORU-10027:buffer overflow limit of 2000 bytes: 使用 dbms_utput.print_line(): 1 ORU-10027:buffer overflow limit of 2000 bytes: 方法1:set serveroutput on size 10000000 //设置大点,默认为2000 bytes 方法2:exec dbms_output.enable(999999999999999999999); //默认为2000

CVE-2016-10191 FFmpeg RTMP Heap Buffer Overflow 漏洞分析及利用

作者:栈长@蚂蚁金服巴斯光年安全实验室 一.前言 FFmpeg是一个著名的处理音视频的开源项目,使用者众多.2016年末paulcher发现FFmpeg三个堆溢出漏洞分别为CVE-2016-10190.CVE-2016-10191以及CVE-2016-10192.网上对CVE-2016-10190已经有了很多分析文章,但是CVE-2016-10191尚未有其他人分析过.本文详细分析了CVE-2016-10191,是学习漏洞挖掘以及利用的一个非常不错的案例. 二.漏洞成因分析 在 RTMP协议中,

HTML5中div、article、section的区别

  最近正在学习html5,刚接触html5,感觉有点不适应,因为有一些标签改变了,特别是div, section article这三个标签,查了一些资料,也试着用html5和css3布局网页,稍微有点头绪了,下边还有一个我刚刚布局好的一个简单的网页,供大家参考,先看一下,最起码心里对html5的结构有些概念. div HTML Spec: "The div element has no special meaning at all." 这个标签是我们见得最多.用得最多的一个标签.本身

优化NFR之一 --MSSQL Hello Buffer Overflow_php基础

1.  前言  3 2.  报警信息  3 3.  NFR的检测  4 4.  协议分析  8 5.  漏洞说明  15 6.  漏洞分析  18 7.  小结  20 1.  前言 NFR(Network Flight Recorder)是一个老牌的商业网络IDS产品,最初由Firewall的牛人Marcus J. Ranum创建,是作为一个通用的网络流量分析和记录软件来实现的,为了最大限度地发挥分析工具的灵活性,NFR提供了完善强大的N-Code脚本语言,在很多的评测中表现出色.虽然L0ph

Dissecting the Disruptor: Writing to the ring buffer

原文地址:http://mechanitis.blogspot.com/2011/07/dissecting-disruptor-writing-to-ring.html(因被墙移到墙内)  作者:Trisha This is the missing piece in the end-to-end view of the Disruptor.  Brace yourselves, it's quite long.  But I decided to keep it in a single blo

进程虚拟地址空间之数据分区存放【转】

转自:http://blog.csdn.net/bullbat/article/details/7318269  作者:bullbat             在前面的<对一个程序在内存中的分析 >中很好的描述了程序在内存中的布局,这里对这个结果做些总结和实验验证.下面以Linux为例(实验结果显示windows上的结果也一样).        我们还是利用前面看到过的这个图,如下图:32位X86机器的内存布局图,内存主要分为栈.堆.BSS段.数据段.代码段5个段.          代码段:

MySQL内核月报 2014.08-MySQL· 参数故事·timed_mutexes

提要 MySQL 5.5.39 Release版本正式从源码里删除了全局参数timed_mutexes.timed_mutexes原本用来控制是否对Innodb引擎的mutex wait进行计时统计,以方便进行性能诊断.为什么要删除这个参数呢? 下面介绍下相关背景: Innodb的同步锁机制 Innodb封装了mutex和rw_lock结构来保护内存的变量和结构,进行多线程同步,考虑可移植性, mutex使用lock_word或者OS mutex来保证原子操作,并使用event条件变量进行阻塞和