缓冲区溢出原理及防范
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
摘要:
正文:
大纲:
1.引言;
随着网络安全技术的飞速发展,缓冲区溢出漏洞已经成为当前最具安全威胁的漏洞之一,缓冲区溢出攻击也成为一种非常有效而常见的攻击方法。如Internet上的第1例蠕虫(Morris)攻击,就是利用了fingerd的缓冲区溢出漏洞。SANS评选出的2005年威胁最大的20个漏洞中,有8个跟缓冲区溢出有关。根据CNCERT最近几周的计算机安全漏洞的统计数据,与缓冲区溢出有关的安全事件占了很大的比例。这些都充分说明了研究缓冲区溢出的重要性。本文主要介绍了windows下的缓冲区溢出的相关知识。
2.漏洞原因和原理;
2.1 产生原因;
当向一个已分配了确定存储空间的缓冲区内复制多于该缓冲区处理能力的数据时,就会发生缓冲区溢出,溢出包括堆溢出和堆栈溢出。它与程序在内存中的分布有关,而它产生的直接原因是由于C/C++程序中的一些函数调用时,没有进行边界检查,如C函数库中的strcpy(),strcat(),sprintf(),gets()等都是不安全的。
由上面的分析可知要产生缓冲区溢出,需要有几个条件: 1) 程序编译时在堆栈上分配了固定大小的缓冲区,并且在对缓冲区进行访问时没有提供边界检查。这条在C/C ++语言中就满足,而对于有边界检查的语言,如Pascal 等,就没有这样的溢出问题。 2) 程序调用了没有进行边界检查的函数来访问(写操作) 缓冲区,这些函数没有对访问的缓冲区的大小进行判断。由于在C语言中,字符串以0字节来标识结尾,其中没有字符串的长度信息,所以几个没有判断字符串长度的字符串拷贝函数就是容易出现问题的函数。这些函数有: strcat()、strcpy()、sprintf()等。 3) 即使程序使用了上面所说的问题函数也不一定会出现溢出漏洞,漏洞发生的最后一个条件是程序员由于粗心,未检查用户输入数据的长度就将其直接复制到缓冲区中去。虽然这看起来是一件小事,很容易杜绝。可惜的是正因为有大量粗心的程序员的存在,使得溢出漏洞变得非常的普遍。
2.2 原理;
图1 堆栈缓冲区示意图
程序的堆栈是先进后出的一种数据结构,堆栈的生长方向适合内存相反的(如图1)。当调用一个函数时,首先是函数的参数逆序进栈,然后将eip里面的内容进栈作为函数的返回地址(ret),即函数调用结束后程序跳转的地址,接着保存现在程序的栈基指针(ebp),并将当前栈顶指针(esp)拷入ebp作为新的基地址.最后将esp减去一定数值用来为本地变量留出一定空间。缓存区往往就分配在这段空间中。
由于堆栈是由内存高地址向内存低地址方向增长,而数组的变量是从内存低地址向高地址方向增长,这时如果没有对数组的越界进行检查和限制,通过向程序的数组缓冲区写入超出其长度的内容,覆盖堆栈原来的返回地址(ret),就会造成缓冲区溢出,从而破坏程序的堆栈。如果构造特殊的注入向量覆盖ret值使程序转而执行恶意代码(shellcode),就达到攻击的目的。
3.基于缓冲区漏洞的攻击过程;
由上所述可知,堆栈缓冲区溢出漏洞的攻击利用的3个步骤是:
1)溢出点ret的定位;定位ret 的流程是用一定格式的字符串覆盖存在溢出漏洞的缓冲区,使程序溢出,然后根据溢出结果计算ret 的位置。最常用的方法是利用报错对话框精确计算出溢出返回点的方法,如图2中的报错对话框所示,可以看到是“0x79797979”覆盖到了溢出点。依次类推,我们可以不断给一个数组反复赋值,利用整除和求余等数学方法来精确计算溢出点的位置。
图2 溢出报错对话框
2)构造shellcode;确定下来溢出点位置后,就需要有可以执行的shellcode来达到入侵的目的。shellcode的编写主要有两种lodsd方法,一是用C等高级语言编写经反汇编后提取二进制码。二是直接使用汇编语言编写并提取二进制码。使用c语言编写生成的代码较长,但编写调试简单,且可以根据不同的需要灵活更改代码。而利用汇编语言生成的代码更为简练,但调试复杂,一旦编写成功后不易修改。
3)用特定地址覆盖ret并且使其跳转到shellcode,并执行。是将返回点覆盖成jmp esp或call ebx的地址。为了通用性使用kernel32.dll中的指令地址.因为同一系统中该模块装载地址变化可能小。覆盖方法主要有两种:①NNNNNNNNNSSSSSSSSSSSRRRRRRRRRRRRRR型。适合于大缓冲区,“N”代表空指令(NOP),也就是0x90,在实际运行中,程序将什么也不做,而是一直延着这些NOPS运行下去,直到遇到不是NOPS的指令再执行之;“S”代表ShellCode;“R”代表覆盖的返回地址,思路是把返回地址R覆盖为nops的大概位置,这样就会跳到Nop中,然后继续执行,直到我们的ShellCode中。但这种方法由于定位不准确,所以使用起来也不准确。②RRRRRRRRRRNNNNNNNNNNNSSSSSSSSSS型。是用大量的“R”填满整个缓冲区,然后大量的Nop,最后是ShellCode。这里,“R”往后跳到Nop中,再顺着往下执行就会到ShellCode中。但在Windows下,“R”中必定会含有0,这样,整个构造就会被截断,所以这种方法只能用于Unix中。
图3 Windows缓冲区分布
Windows的系统核心dll包括kernel32.dll、user32.dll、gdi32.dll。这些dll一直位于内存中,而且对应于固定的版本,Windows加载的位置是固定的。用系统核心dll中的jmp esp 地址来覆盖返回地址,而把ShellCode紧跟在后面,这样就可跳转到我们的ShellCode中。
其利用格式是NNNNNNRSSSSSS,N=Nop,S=ShellCode,R=jmp esp的地址。如图3所示。常用的JMP ESP的地址:0x7ffa4512(winXP/win2003通用);又是我们还可以利用JMP EBX的地址:0x7ffa1571(winXP/win2003通用);
4.检测方法及防范措施;
根据缓冲区溢出攻击的步骤,可将常用的缓冲区溢出攻击检测技术分为以下3 种类型:基于输入字符串的检测方法,基于保护堆栈中的返回地址的检测方法和基于监视系统调用的检测方法。
4.1 基于输入字符串的检测方法
对输入的字符串进行检测,确定其为溢出攻击字符串时采取阻拦措施,使攻击者无法注入攻击代码。一般有以下3 种方法构建溢出攻击字符串。分别如下图4-1,图4-2,图4-3所示:
图4-1 缓冲区大于ShellCode 长度
图4-2缓冲区小于ShellCode 长度
图4-3将ShellCode 放在环境变量里
第1 种溢出攻击字符串适用于缓冲区大于ShellCode 长度的情况;第2 种溢出攻击字符串一般用于缓冲区小于ShellCode 长度的情况;第3 种方法是将ShellCode 放在环境变量里,是目前较为常用的方法。
在第1 种和第2 种类型的溢出攻击字符串中ShellCode前都加了若干的NOP 指令,因为这2 种情况下ShellCode 的地址无法确定,但只要返回地址指向ShellCode 前的任一条NOP 指令,ShellCode 就可以执行,大大增加了ShellCode 执行的可能性。这些NOP 指令称为sledge。其他单字节指令如AAA 等也可构成sledge。因此缓冲区溢出攻击检测系统可以通过检查输入的字符串中是否含有大量NOP 等可构成sledge的指令来判断此字符串是否是溢出攻击字符串。不过这种方法并不适用于检测第3 种类型的攻击。但这3 种类型的攻击字符串中都含有ShellCode。因此,确定出ShellCode 的基本特征,如不含有“0x00”,含有某些特殊的系统调用等,然后利用人工智能、模式匹配、规则匹配等方法检查输入字符串中是否包含ShellCode 也可检测出是否有缓冲区溢出攻击发生。这些检测都可以在入侵