版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
内容备注《网络攻防原理与技术》课程教案讲课题目:第九讲缓冲区溢出攻击目的要求:了解缓冲区溢出攻击的基本概念;掌握缓冲区溢出攻击的基本原理;了解shellcode的构造方法;了解缓冲区溢出攻击的主要防护方法。重点难点:缓冲区溢出攻击的基本原理。方法步骤:理论讲授。器材保障:电脑、投影仪。主要教学内容:一、缓冲区溢出攻击概述缓冲区溢出的根源在于程序员没有对输入数据进行严格的边界检查。如果缓冲区被写满,而程序没有去检查缓冲区边界,也没有停止接收数据,这时就会发生缓冲区溢出。在非恶意情况下,缓冲区溢出一般会造成进程的状态紊乱,执行流程失去控制,最终进程通常会因为内存读写问题被操作系统杀死。如果攻击者精心构造写入数据,则当溢出发生后,攻击者可以精确执行其预定的代码,实现攻击的目的。缓冲区溢出的缺陷普遍存在并且容易挖掘,而且利用成功后常常可以直接获得进程特权,甚至控制整台主机,因此缓冲区溢出攻击在安全攻击中占有很大的比重。缓冲区溢出不仅仅局限于非安全类型编程语言C/C++,安全类型的编程语言代码(如Java,Perl)的底层基础同样面临缓冲区溢出攻击的威胁。二、缓冲区溢出攻击原理(一)进程的内存结构在大多数操作系统中,系统在创建一个进程时,会一次性给该进程分配一块内存(通常称为“静态分配”),这块内存在进程运行期间保持不变,主要由四部分组成:1)文本(Text)段,保存程序的所有指令(操作码+操作数)。这个内存区域通常被标记为只读。2)数据(Data)段,保存初始化的全局静态数据。3)BSS(BlockStartedbySymbol)段,保存未初始化的全局数据。4)堆栈(Stack),保存动态变量和函数调用的现场数据(主要包括函数的返回地址、函数参数、栈帧指针等),简称为“栈”。进程刚启动时,栈空间是空的,里面没有实体。在进程运行期间,对具体实体的栈分配是进程自行生成(压栈)和释放(弹出)实体,系统并不参与。只要压入的实体的总长度不超过栈空间尺寸,栈分配就与系统无关。如果超过了,就会引发栈异常。除了上述一次性分配的内存外,进程还可以动态申请内存,这就是堆(Heap)分配。当进程需要生成实体时,向系统申请分配空间;不再需要该实体时,可以向系统申请回收这块空间。用户进程使用特定的函数,如malloc(),calloc(),realloc(),new()等申请堆块。由于是按需分配,因此堆的空间利用率最高。缓冲区溢出攻击一般包含两个主要步骤。首先在程序中植入攻击代码或植入攻击代码所需的攻击参数(如果攻击代码已存在于目标程序中),然后改变程序的执行流程,转去执行攻击代码。根据是否需要植入攻击代码,可将缓冲区溢出攻击分为两种攻击模式。第一种模式称为代码注入攻击。在这种模式下,攻击者向缓冲区写入的数据包含了攻击代码(可执行的二进制代码,通常称为“shellcode”),当发生缓冲区溢出时,溢出的数据覆盖掉一个可执行程序的入口地址(如函数的返回地址,函数指针变量等等),使得该地址指向shellcode,从而当程序试图通过该入口地址执行代码时,就会执行攻击者的shellcode。第二种模式下,攻击者想要的攻击代码已经在被攻击的程序中了,攻击者所要做的只是为攻击代码传递它所需要的参数,然后用一个系统函数的地址覆盖可执行代码的入口地址,通过巧妙的构造可以使程序用预设的参数调用系统函数。第二种攻击模式的出现是因为第一种模式需要栈或堆内存具有可执行属性,而随着缓冲区溢出攻击日益猖獗,出现了不可执行的堆或栈的概念,有些操作系统(比如Linux)采取了对应的补丁策略使得代码注入攻击不可行,而第二种攻击模式不受此限制。一般来说,根据缓冲区溢出发生的位置可以将缓冲区溢出漏洞分成:栈溢出,堆(Heap)溢出,静态数据段(BSS)溢出。栈溢出一个堆栈包括:一块连续的内存块,一个堆栈指针(SP)指向堆栈的栈顶,一个基址指针(BP)保存具有固定偏移的局部变量和函数参数的基地址。在Intel处理器中,SP保存在寄存器esp中,BP保存在寄存器ebp中。栈支持两种操作,压栈(PUSH)和弹出(POP)。PUSH是将数据放到栈的顶端,POP是将栈顶的数据取出。压栈和弹出操作均会自动改变esp的值,即栈顶发生变化。在高级语言中,程序函数调用和函数中的临时变量都用到栈,参数的传递和返回值也通过栈来实现,通常对局部变量的引用是通过给出它们相对BP的偏移量来实现的。当程序中发生函数调用时,计算机做如下操作:首先把参数压入堆栈;其次把指令寄存器(IP,在Intel处理器中称为eip)中的内容压栈,作为返回地址(ret);第三个放入栈的是基址寄存器(BP);然后把当前的栈指针(SP)拷贝到BP,作为新的基地址;最后把SP减去适当的数值,为本地变量留出一定空间。调用者完成压栈操作后,调用函数。函数被调用以后,在栈中取得数据,并进行计算。函数计算结束以后,调用者或者函数本身修改栈,使栈恢复平衡。一般来说,根据覆盖数据的内容不同,可能会导致以下几种结果:(1)覆盖了其他局部变量。如果被覆盖的局部变量是条件变量,那么可能会改变函数原本的执行流程,这种方式可以用于破解简单的软件验证。(2)覆盖了ebp的值。修改了函数执行结束后要恢复的栈指针,将会导致栈帧失去平衡,即函数调用关系及相关现场信息被破坏,程序无法正常执行下去。(3)覆盖了返回地址。通过覆盖的方式修改函数的返回地址,使程序代码执行“意外”的流程。如果用于溢出的数据内保存了一系列指令的二进制代码(shellcode),一旦栈溢出修改了函数的返回地址,并将该地址指向这段二进制代码的起始位置,程序将转而执行攻击者植入的shellcode。这就是栈溢出攻击原理的核心所在。(4)覆盖参数变量。修改函数的参数变量可能改变当前函数的执行结果和流程。(5)覆盖上级函数的栈帧。这种情况与第(4)种情况类似,只不过影响的是上级函数的执行。当然这里的前提是保证函数能正常返回,即函数地址不能被随意修改,而要实现这一点就比较复杂了。对于第(3)种情况,理论上能完成栈溢出攻击,但是实际利用过程很难直接跳转到shellcode地址。操作系统每次运行程序分配的栈地址是不确定的,因此存放在栈上的shellcode地址也不确定,很难通过硬编码的方式覆盖新返回地址。为了能准确定位shellcode的地址,需要借助一些额外的操作,其中比较经典的是借助跳板的栈溢出方式。缓冲区溢出攻击最关键的步骤就是使目标程序执行攻击者植入的shellcode。其功能一般是在目标主机上为攻击者提供远程控制接口,如开放远程shell(Unix系统中的命令解释程序)或远程命令行窗口(Windows系统中的cmd)。一般来说,shellcode应满足以下要求:(1)shellcode中可能不允许出现一些特殊字符。例如,对于针对strcpy类函数引起的缓冲区溢出攻击,shellcode中不允许有任何的NULL字符(”\x0”),这是因为NULL字符是字符串的结束符。(2)shellcode应短小精悍,这主要是由于缓冲区大小有限。(3)shellcode与操作系统有关,Windows系统上的shellcode不能用于Linux系统。shellcode的编写比较复杂,一般有两种方法。一种是使用汇编语言编写,用汇编编译器编译后得到二进制代码;另外一种是在C语言中嵌入汇编代码,使用C语言编译器编译,然后从编译过的可执行文件中提取二进制可执行代码,得到shellcode。堆溢出堆是由进程动态分配的内存区。进程通过malloc类函数分配堆内存,通过free类函数释放。如果进程没有主动调用对应的free类函数来释放所申请的堆内存空间,这些堆内存空间会一直保留到进程终结才会由操作系统来执行释放操作(有些高级语言,如Java,有自动收集并释放无用内存机制,无需等到进程结束才释放)。堆由很多内存块组成,其中一些已分配使用,一些是空闲的。与堆栈的增长方向相反,在大部分的系统(包括Linux系统)中,堆是向上增长的(向高地址方向增长)。利用堆溢出漏洞实现攻击的难点之一是不同的操作系统采用的堆内存管理机制不同,甚至相同的操作系统如果版本不同也会有不同的堆内存管理机制,这使得攻击代码的通用性比较差。攻击者要利用堆缓冲区溢出漏洞进行攻击,就需要针对不同的堆管理机制编写不同的攻击代码和利用程序,但这仅仅是工作量的问题。BSS段溢出BSS用于存放未初始化的全局变量。在写入数据前,它始终保持全零。由于在考虑缓冲区溢出攻击时,堆和BSS段具有相近的特性,因此提到的“基于堆的溢出”既包含堆的溢出,也包含BSS段的溢出。在大部分系统(包括Linux系统)中,BSS段也是向上增长的(向高地址方向增长)。BSS段存放全局和静态的未初始化变量,变量与变量之间是连续存放的,没有保留空间。这样定义的两个字符数组即是位于BSS段:staticcharbuf1[16],buf2[16];如果事先向buf2中写入16个字符A,之后再往buf1中写入24个B,由于变量之间是连续存放的,静态字符数组buf1溢出后,就会覆盖其相邻区域字符数组buf2的值。利用这一点,攻击者可以通过改写BSS中的指针或函数指针等方式,改变程序原先的执行流程,使指针跳转到特定的内存地址并执行指定操作。(四)其他溢出整数类型规定了整型变量能存放的数值范围是固定的,当试图用一个大于或小于其取值范围的数值对其进行赋值时,或者诸如加、减、乘、左移和类型转换等操作使得其结果超出相应的范围,使得计算结果是期望值与相应类型极值的取模,这类计算结果的失真称为整数溢出。不同符号或宽度间的类型转换可能导致计算结果失真或数值被误解,也会引发整数溢出。当溢出的整数变量用作其他类似于数组下标或者数组访问的边界控制时,它们就有可能被攻击者间接利用实施诸如恶意代码执行、拒绝服务等攻击行为。由于格式化字符串函数的参数个数的不确定性,使得攻击者可以利用它来进行读写攻击,改变或读取指定内存的内容。函数sprintf(char*str,constchar*format,...)将格式化的数据写入str所指的数组中,并添加‘\0’,如果格式化的数据长度超出了数组的容量就会溢出。缓冲区溢出攻击的防护(一)基本思路攻击者要利用缓冲区溢出漏洞进行攻击,两个必要条件必须满足:第一,存在缓冲区溢出漏洞;第二,这个缓冲区溢出漏洞必须是可以利用的。防御者的目的就是破坏这两个条件中的一个或者两个。防御者可以通过某种手段,比如检测,在程序发布之前找到其中的漏洞并给予修复,从而使得第一个必要条件不成立。防御者也可以通过一定方式,比如修改堆栈机制,使得即使程序中存在缓冲区溢出漏洞,也无法利用这种漏洞(最多使程序出现内存访问错误,导致拒绝服务攻击),使得第二个条件不成立。据此,可以将缓冲区溢出漏洞的防御方式分为两种:主动式防御和被动式防御。(二)主动式防御解决缓冲区溢出攻击最直接的方式是确保程序中不存在缓冲区溢出漏洞。为达到这一目的,主要有以下几种措施。1)使用安全的编程语言绝大多数软件中的缓冲区溢出漏洞源于C/C++语言的不安全性。C/C++语言本身不对缓冲区边界进行限定,主要目的是提供最高的效率。程序员在编写程序的时候,必须记住缓冲区是有边界的,对缓冲区的读写不能超出其边界。2)替换不安全函数将现存的代码采用新的编程语言重写是不现实的。一种可行的解决方案是,将程序中的一些不安全函数替换成安全的函数,如用strncpy替换strcpy,用strncat替换strcat。这些安全的函数将对缓冲区读写进行严格的边界检查。3)养成良好的编程习惯如果采用C/C++语言进行程序开发,避免缓冲区溢出漏洞的最根本的解决办法是程序员加强安全意识,在每次进行缓冲区操作时一定要进行严格的边界检查。(三)被动式防御溢出漏洞不可避免,那么可采取措施使得漏洞不可用,这就是被动防御方法。以下是几种著名的被动防御技术。1.栈和堆不可执行大多数情况下,shellcode被注入到堆中或者栈中,如果使堆和栈不具有可执行属性,则可以防御代码注入的攻击。这种方法的优点是对性能影响小,缺点是需要修改并重新编译内核。由于ret2libc的旁路攻击不需要执行堆和栈中的代码,所以这种方法对非代码注入型溢出攻击无效。此外,有些程序本身可能被设计成需要执行堆或者栈空间中的代码,使得这种方式存在软件兼容性问题。2.StackGuardStackGuard对编译器进行扩展,调用函数时在堆栈的局部变量和函数地址之间存放随机产生的4B的“canary”字,在函数返回之前检查“canary”的完整性。这种方法的缺点是:需要源码;由于每调用一次函数都要对“canary”进行一次校验,程序效率会下降;不能保护函数参数、局部变量、栈指针及位于堆中的数据;无法阻止堆溢出攻击;通过破坏堆栈中旧帧指针或局部指针变量可以绕过这种安全机制。3.StackShieldStackShield是创建一个特别的堆栈用来存储函数返回地址的一份拷贝。它在受保护的函数的开头和结尾分别增加一段代码,开头处的代码用来将函数返回地址拷贝到一个特殊的表中,而结尾处的代码用来将返回地址从表中拷贝回堆栈。这种技术需要在编译阶段使用,需要源代码;有效率损失;不能保护函数参数、局部变量、栈指针及位于堆中的数据,不能抵抗基于堆溢出的攻击。4.PointGuardPointGuard的基本思想是:攻击者利用缓冲区溢出漏洞只会溢出修改堆、栈、数据段上的数据,而不会修改存储在寄存器中的数据,内存中如果存放的是加密后的数据,攻击者就无法随心所欲地进行攻击。PointGuard对指针型数据进行加密后再将其存放在内存中,指针引用前再在寄存器中解密。5.ProPoliceProPolice使用一个修改过的编译器在函数调用中插入一段检测代码以检测堆栈溢出。不同的是,它对堆栈中的局部变量的位置进行重新排序,让字符型缓冲区紧挨着旧基指针的栈底,并复制函数参数中的指针,以便它们排在任何数组之前,使得该缓冲区溢出时不会修改函数指针。但是,ProPolice不能保护位于堆中的数据,也无法抵抗基于堆溢出的攻击。6.拦截脆弱函数Libsafe重新编写了C库中不安全的函数,先于C库安装。在运行时拦截所有对具有缓冲区溢出风险的库函数的调用,并调用具有边界检查功能的安全函数来完成原功能。如果边界检查不通过,就报警并终止进程。7.打乱和加密这种技术使得攻击者的恶意代码即使成功注入目标软件,也不能运行,从而有效防止通过注入恶意代码来实施破坏。很多攻击都基于内存分配的规律,一般需要知道攻击代码在内存中的存储位置或者将要覆盖内存的哪些区域。基于这个原因,改变传统的内存分配算法,在程序加载时,将内存布局打乱,从而有效地防御缓冲区溢出的攻击。基于堆栈的缓冲区溢出攻击一般需要了解系统调用映射或库的入口点,通过随机化堆栈起点、随机化系统调用映射和改变库的入口点可以防御缓冲区溢出攻击。加密可执行文件,在程序执行前解密,同样可以防御采用注入攻击代码的方式进行的缓冲区溢出攻击。当运行到攻击代码时,因为攻击代码事先没有加密,故采用解密操作对其进行处理后代码不能正确运行。8.硬件增强方式硬件增强是最底层的技术,如果能用于检测和预防缓冲区溢出漏洞
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 年产xx再生铝冶炼项目建议书
- 原味蛋项目可行性研究报告
- 区域地理 世界地理 北美和美国
- 夫妻沙盘心理治疗
- 大班生成活动教案:会飞和不会飞
- 胸椎前路手术
- Perforce:2024游戏技术现状报告
- 小班社会教案及教学反思《抱一抱》
- 肺源性心脏病护理病例讨论
- 一年级下册数学教案-4.1.3 100以内数的认识∣人教新课标
- 11_tkinter GUI编程教学课件
- 扁平化组织结构讲解PPT课件
- 2013年国际头痛新分类和诊断标准
- 比亚迪汽车企业战略(swot分析)
- 工作交接表(模版)
- 高中学生社区服务活动记录表
- 数据模型与决策案例分析报告
- EXCEL桥涵水文计算公式大全
- 标签打印机验证方案(共20页)
- XRF定性和定量分析ppt课件
- 运营高速公路风险评价报告
评论
0/150
提交评论