缓冲区溢出分析第03课:缓冲区溢出的利用

前言

上次我们已经讨论了缓冲区溢出的原理,而这次我们需要利用这个原理来构造条件,从而利用这个漏洞。

其实缓冲区溢出漏洞的利用主要是需要解决以下三个问题:

1、精确定位返回地址的位置

2、寻找一个合适的地址,用于覆盖原始返回地址

3、编写Shellcode到相应的缓冲区中

这次我们就结合实验程序,来解决上述三个问题,实现漏洞的利用。

精确定位返回地址的位置

缓冲区溢出利用的第一步,就是需要我们精确定位返回地址的位置。由于我们这次的程序比较简单,所以通过错误提示对话框,我们就能够判定返回地址的位置了。

我们首先运行一下OverrunTest_2.exe,经过上次的分析我们知道,由于返回地址被覆盖,而新地址是一个无效地址,所以使得main函数执行完后不知道该执行什么指令,于是就出现了错误对话框。一般来说,我们要么单击“发送错误报告”,要么单击“不发送”,但是这次我们选择单击以蓝色字体显示的“请单击此处”。然后再选择“要查看关于错误报告的技术信息,请单击此处”,就弹出了“错误报告内容”对话框:

图1

在这个报告中,有两个地方比较重要。一个是“Code”,即错误代码。这里是“0xc0000005”,表示出现了缓冲区溢出的错误。另外一个是“Address”,即错误发生的地址,这里是“0x00006579”,而结合上次的OD分析,我们知道由于这是一个不存在的地址,从而导致了程序出错。

借助于OD,我们可以很容易弄清楚返回地址的位置,如果没有OD该怎么办呢?对于这个实验程序来说,由于我第一次输入“jiangye”的时候,程序没出问题,而第二次输入“jiangyejiangye”时就报错了,报错内容是“0x00006579”这个地址出现了问题,而我们将这个地址翻译为英文字母,可以发现“0x65”代表的是“e”,而“0x79”则代表的是“y”。正好是我所输入的那一长串字符的最后两个字母。由于地址是由4个字节表示的,那么对于这个程序而言,如果我将全局变量name赋值为“jiangyejiangXXXX”,那么这最后的四个“X”就正好覆盖了返回地址,而前面的12个字符可以是任意字符。那么我们也就解决了缓冲区漏洞利用的第一个问题——精确定位返回地址的位置。

但是这里还有一个问题需要说明的是,我们这个程序中的局部变量,也就是buffer只有8个字节,因此很容易就能够被填充满,从而很容易就能够被定位,但是如果缓冲区空间很大,该如何定位呢?不能还是一直以“jiangyejiangye……”这样不断地测试下去吧。其实定位溢出位置的方法有很多,在此可以告诉大家一个便于初学者理解的方法,我们可以利用一长串各不相同的字符来进行测试,比如:

TestCode[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

利用26个英文大写字符与26个小写字符,一共是52个字母进行测试,这样一来,一次就可以验证52个字节的缓冲区空间。这里将我们的局部变量数组的大小修改为80个字节,然后利用这个方法进行验证。首先测试一段上述字符,可见程序没有什么变化,那么我们就再加上一段,变成104个字符进行验证,可见此时已经弹出了错误提示对话框:

图2

在Address后可以发现,其值为0x6a696867,注意我们的系统是小端显示,也就是说,实际的字符应该是0x67、0x68、0x69、0x6a。那么把它转换成字母,可以知道是g、h、i、j,由于我们这里是使用了两轮验证字符,第一轮是52个字符,加上第二轮的前26个大写字符,就是78个字符,然后小写字母g前面还有6个字符,那就是84个字符,注意这里还包含4个字节的EBP,所以我们所验证的缓冲区的大小就是80个字节的空间了。

关于其它的一些判断缓冲区空间大小的方法,我们会在以后的实际分析中再进行讲解。我觉得大家目前懂得这个办法就可以了。甚至还可以将键盘上的标点符号也加到TestCode里面,可以按照ASCII码表的顺序进行排列,这样一次就能够验证更多的空间了。

寻找一个合适的地址,用于覆盖原始返回地址

现在我们需要做的工作是确定“jiangyejiangXXXX”中的最后四个“X”应该是什么地址。这里我们不能凭空创造一个地址,而是应该基于一个合法地址的基础之上。当然我们通过在OD中的观察,确实能够找到很多合适的地址,但是这种方法不具有通用性,毕竟要找一个确切的地址还是不那么方便的。解决这个问题的方法有很多种,但是最为常用最为经典的,就是“jmp esp”方法,也就是利用跳板进行跳转。

这里的跳板是程序中原有的机器代码,它们都是能够跳转到一个寄存器内所存放的地址进行执行,如jmp esp、call esp、jmp ecx、call eax等等。如果在函数返回的时候,CPU内寄存器刚好直接或者间接指向ShellCode的开头,这样就可以把对栈内存放的返回地址的那一个元素覆盖为相应的跳板地址。

不妨使用实际的例子来说明一下。我们先研究一下正常的程序,也就是OverrunTest_1.exe,用OD载入,执行到main函数最后的位置,即retn语句处,此时我们关注一下esp寄存器所保存的值:

图3

可以发现,现在esp中保存的值是0x0012FF84这个地址,而在栈中这个地址对应的就是我们的返回地址0x00401699,即我们下一条语句的位置。然后我们此时再按一下F8,单步执行,那么此时main函数就会执行完毕:

图4

可见此时我们已经跳出了main函数,来到了0x00401699位置处的语句进行执行,我们这里不用管它的具体执行内容,主要关注esp的值。可以发现,esp的值由刚才的0x0012FF84变成了0x0012FF88,从栈空间来看,即刚才那个值的下一个位置。不要忘了,0x0012FF84位置正式我们想要修改的返回地址的位置。

总结一下,我们可以得知,当main函数执行完毕的时候,esp的值会自动变成返回地址的下一个位置,而esp的这种变化,一般是不受任何情况影响的。既然我们知道了这一个特性,那么其实就可以将返回地址修改为esp所保存的地址,也就是说,我们可以让程序跳转到esp所保存的地址中,去执行我们所构造的指令,以便让计算机执行。

当然了,以上我所讲的是正常的情况,也就是返回地址没有被破坏的情况,那么如果返回地址被破坏了,esp还具备这种特性吗?不妨再用OD载入OverrunTest_2.exe这个存在缓冲区溢出问题的程序,来研究一下,首先还是先来到main函数的retn的位置:

图5

可以看到,与正常情况相比,esp中所保存的地址是一样的,只是该地址中保存的值不同而已,然后按F8单步执行:

图6

可见除了此时反汇编代码窗口为空以外,其它位置还是没什么变化的,特别是esp的值,依旧是自增了4,在栈窗口来看,也就是来到了返回地址下方的位置。那么也就说明了,我们完全可以利用esp的这一特性来做文章。

那么如何让程序跳转到esp的位置呢?我们这里可以使用“jmp esp”这条指令。jmp esp的机器码是0xFFE4,那么我们可以编写一个程序,来在user32.dll中查找这条指令的地址(当然了,jmp esp在很多动态链接库中都存在,这里我只是以user32.dll作为例子):

#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("OPCODE 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;
}

然后运行这个程序,查找jmp esp:

图7

可以看到,这里列出了非常多的结果,我们可以随便取出一个结果用于我们的实验,这里我选择的是倒数第二行的0x77e35b79。也就是说,我需要使用这个地址来覆盖程序的返回地址。这样,程序在返回时,就会执行jmp esp,从而跳到返回地址下一个位置去执行该地址处的语句。

请大家注意的是,有很多种方法可以获取jmpesp,而且不同的系统这个地址可能是不同的,但是有些地址是跨系统的,大家可以自行在网上搜索。

编写Shellcode到相应的缓冲区中

至此可以先总结一下我们即将要编写的“name”数组中的内容,经过分析可以知道,其形式为“jiangyejinagXXXXSSSS……SSSS”。其中前12个字符为任意字符,XXXX为返回地址,这里我使用的是0x77e35b79,而SSSS……SSSS则是我们具体想让计算机执行的代码。

由于ShellCode的编写涉及不少知识,所以我将这个内容留到下节课再进行论述。

时间: 2024-10-10 15:28:31

缓冲区溢出分析第03课:缓冲区溢出的利用的相关文章

缓冲区溢出分析第06课:W32Dasm缓冲区溢出分析

漏洞报告分析 学习过破解的朋友一定听说过W32Dasm这款逆向分析工具.它是一个静态反汇编工具,在IDA Pro流行之前,是破解界人士必然要学会使用的工具之一,它也被比作破解界的"屠龙刀". 但是即便是这么一款破解界的"神器",竟然也是存在着缓冲区溢出的漏洞的.可见,它在破解无数程序的同时,其自身也存在着被"黑"的风险.那么我们可以首先分析一下漏洞报告: ##############################################

缓冲区溢出分析第08课:MS06-040漏洞研究——动态调试

前言 经过上次的分析,我们已经知道了MS06-040漏洞的本质,那么这次我们就通过编程实现漏洞的利用. 编写漏洞利用程序的框架 这里我使用的是VC++6.0进行编写,需要将包含有漏洞的netapi32.dll文件与工程文件放置在同一个目录下.程序如下: #include <windows.h> typedef void (*MYPROC)(LPTSTR, ...); int main() { char Str[0x320]; char lpWideCharStr[0x440]; int arg

缓冲区溢出分析第11课:整数溢出的原理

<缓冲区溢出分析>这一系列的内容是我为"i春秋"(www.ichunqiu.com)所录制的同名视频课程的讲稿汇总.每次我都是在写完课程的文档后,再依据文档内容进行课程的讲解.而本系列的内容也是从零开始,来给大家由浅入深地进行缓冲区溢出漏洞的讲解.整个课程是理论与实践相结合,每讲完几个基础理论后,都会配以实际的软件中的漏洞进行分析,以帮助大家更好地理解漏洞的原理.有兴趣的朋友可以结合本文与配套视频进行学习. 前言 我们之前所研究的漏洞,都是非常经典的栈溢出漏洞,也是最为常见

缓冲区溢出分析第10课:Winamp缓冲区溢出研究

<缓冲区溢出分析>这一系列的内容是我为"i春秋"(www.ichunqiu.com)所录制的同名视频课程的讲稿汇总.每次我都是在写完课程的文档后,再依据文档内容进行课程的讲解.而本系列的内容也是从零开始,来给大家由浅入深地进行缓冲区溢出漏洞的讲解.整个课程是理论与实践相结合,每讲完几个基础理论后,都会配以实际的软件中的漏洞进行分析,以帮助大家更好地理解漏洞的原理.有兴趣的朋友可以结合本文与配套视频进行学习. 前言 Winamp是一款非常经典的音乐播放软件,它于上世纪九十年代

缓冲区溢出分析第01课:缓冲区溢出分析导论

前言 <缓冲区溢出分析>这一系列的内容是我为"i春秋"所录制的同名视频课程的讲稿汇总.每次我都是在写完课程的文档后,再依据文档内容进行课程的讲解.而本系列的内容也是从零开始,来给大家由浅入深地进行缓冲区溢出漏洞的讲解.整个课程是理论与实践相结合,每讲完几个基础理论后,都会配以实际的软件中的漏洞进行分析,以帮助大家更好地理解漏洞的原理. 课程导论 漏洞指的是在硬件.软件.协议的具体实现或系统安全策略上存在的缺陷,通常是由程序的编写者在编写时的疏忽造成的.漏洞的存在使攻击者能够

缓冲区溢出分析第02课: 缓冲区溢出的原理

创建含有缓冲区溢出隐患的程序 这里我们为了达到实验的要求,先来编写一个最简单的存在缓冲区溢出隐患的程序.这个程序我使用VC++6.0进行编写,并在Windows XP下执行.(这里请大家注意的是,如果你使用的是新版本的VC,由于微软加入了GS机制来防止缓冲区溢出情况的出现,那么本实验就无法实现.) 首先新建一个Win32控制台应用程序,然后输入以下C语言代码: #include "stdio.h" #include "string.h" char name[] =

缓冲区溢出分析第09课:MS06-040漏洞研究——深入挖掘

前言 经过前两次的分析,我们已经对Netapi32.dll文件中所包含的漏洞成功地实现了利用.在系统未打补丁之前,这确实是一个非常严重的漏洞,那么打了补丁之后,这个动态链接库是不是就安全了呢?答案是否定的.即便是打了补丁,虽说我们之前所分析的漏洞已被补上,但是这个程序中依旧存在着其它的问题. 对漏洞函数进行静态分析 我们之前所研究的Netapi32.dll的大小为309,008 字节,补丁后的文件大小为309,760 字节.我们用IDA Pro载入打过补丁后的DLL文件,找到之前出现问题的函数位

缓冲区溢出分析第07课:MS06-040漏洞研究——静态分析

前言 我在之前的课程中讨论过W32Dasm这款软件中的漏洞分析与利用的方法,由于使用该软件的人群毕竟是小众群体,因此该漏洞的危害相对来说还是比较小的.但是如果漏洞出现在Windows系统中,那么情况就会很不一样了.毕竟Windows统治了全球九成以上的计算机操作系统,因此如果该系统中出现了漏洞,而这个漏洞又被别有用心者所利用,那么就必然会出现数以亿计的受害者. MS06-040漏洞基本信息 这次我们讨论的漏洞是在2006年8月8日,由微软公布的MS06-040这个漏洞(https://techn

缓冲区溢出分析第04课:ShellCode的编写

前言 ShellCode究竟是什么呢,其实它就是一些编译好的机器码,将这些机器码作为数据输入,然后通过我们之前所讲的方式来执行ShellCode,这就是缓冲区溢出利用的基本原理.那么下面我们就来编写ShellCode.为了简单起见,这里我只想让程序显示一个对话框: 图1 获取相关函数的地址 那么我们下面的工作就是让存在着缓冲区溢出漏洞的程序显示这么一个对话框.由于我在这里想要调用MessageBox()这个API函数,所以说首先需要获取该函数的地址,这可以通过编写一个小程序来获取: #inclu