缓冲区溢出攻击方式以及防范策略

时间:2022-10-09 12:14:05

【前言】缓冲区溢出攻击方式以及防范策略由文秘帮小编整理而成,但愿对你的学习工作带来帮助。(Dept. of Computer and Information Engineering, Heze University, Heze 274015, China) Abstract: Buffer overflow is widely used for network attack mode, this paper analyzes the basic principle of the buffer overflow attack, according to the stack ove...

摘要:缓冲区溢出是被广泛利用的网络攻击方式,该文分析缓冲区溢出攻击的基本原理,针对堆栈溢的攻击方式列举实例并分析其攻击方法,并根据该方法提出防范缓冲区溢出的措施。

关键字:缓冲区溢出攻击;堆栈;防御

中图分类号:TP393文献标识码:A文章编号:1009-3044(2011)29-7117-02

Buffer Overflow Attack and Guard Strategy

HUANG Wen-qing

(Dept. of Computer and Information Engineering, Heze University, Heze 274015, China)

Abstract: Buffer overflow is widely used for network attack mode, this paper analyzes the basic principle of the buffer overflow attack, according to the stack overflow attack examples and analysis of its attack method, and according to the method proposed the measures of prevention of buffer overflow.

Key words: buffer overflow attack; stack; defense

1 缓冲区溢出攻击的原理

缓冲区是进程在计算机内存中的一个连续的地址空间,在这个空间中保存了给定的数据,当进程向缓冲区中写入超过地址空间长度的数据时,就会使缓冲区产生溢出,从而破坏程序的堆栈,从而运行其他指令达到攻击的目的。造成缓冲区溢出的原因有很多,但归根结底是因为程序设计过程中没有检查用户输入的参数是否符合计算机运行是的要求。

2 缓冲区溢出攻击方式

缓冲区溢出的攻击方式一般可分为以下四种情况:

1)栈溢出(stack smashing)

本方法是进程中断执行过程中利用strcpy()等函数覆盖堆栈。由于进程执行过程中未检查输入缓冲区的数据长度,攻击者找到溢出点后,输入超过缓冲区长度的数据(包括精心构造的填充缓冲区长度用的空指令以及shellcode编码),导致地址越界,越界部分覆盖栈中局部变量空间之上的栈桢指针EBP以及函数返回retaddr的地址,并恰好使shellcode覆盖EIP,当函数执行返回中断之前的指令ret指令时,retaddr从栈中弹出,作为返回指令的地址赋给EIP寄存器,EIP寄存器便由本来执行原进程中断之前的流程改变成了执行事先编辑好的shellcode(如图1所示)。

2)堆溢出(malloc()函数)

堆溢出是利用函数溢出来进行攻击。malloc()函数又叫动态内存分配函数,功能是分配长度为num_bytes字节的内存空间给应用程序。堆溢出方式和传统的栈溢出一样,当输入超出malloc()预先分配的内存空间大小时候,就会覆盖掉这段空间之后的一段存储区域,如果该存储区域有一个重要的变量比如euid(有效用户标示),那么就可以用它来导致攻击。另一种是典型的double-free堆腐败,在内存回收操作中,合并相邻空闲块重新插入双向链表时会有一个写4字节内存的操作,如果弱点程序由于编程错误free()一个不存在的块,就可以精心伪造这个块,从而覆盖任何想要的值:函数的返回地址、库函数的plt地址等。

例如,通过覆盖Windows 系统中异常处理链SEH来实现的(如图2所示)。在Windows系统中,当发生异常时,系统就会查找异常处理链SEH,找应对这种异常的处理程序,找到了对应的处理程序后,就把保存的处理程序的地址赋给EIP,这样系统就会执行处理程序,以避免系统崩溃。

在Windows系统中,SEH链保存在堆栈中。所以这种SEH覆盖方式,就是将顶层SEH中的指向下一节点的值覆盖成JMP 04,而处理程序的地址覆盖成call ebx的地址,后面再跟上shellcode。这样出错后,Windows就会跳转到处理程序中,执行call ebx t,进入前面的jmp 04中,再执行jmp 04就正好跳过覆盖的地址,到达shellcode 处(如图3所示)。

3)格式化字符串漏洞(format string vulnerability)

如果输入的字符串格式由用户定制,攻击者就可以伪造任意格式串,利用*printf()系列函数的特性就可以窥探堆栈空间的内容,超常输入可以引发传统的缓冲区溢出,或是用“%n”覆盖指针、返回地址等。

例如:打印输出一个字符串或者把这个串拷贝到某缓冲区内。写出如下的代码:

printf("%s", str);

但是假如程序员为了节约时间和提高效率,并在源码中少输入6个字节,会这样写:

printf(str);

在这种情况下,printf的第一个参数无论如何都会输出的,但是程序编写者打开了一个安全漏洞,可以让攻击者控制程序的执行。程序员编写者传入了一个逐字打印的字符串,实际上该字符串被printf函数解释为一个格式化字符串(format string)。函数在其中寻找特殊的格式字符比如"%d"。如果碰到格式字符,一个变量的参数值就从堆栈中取出。攻击者可以通过打印出堆栈中的这些值来偷看程序的内存,甚至允许向运行中程序的内存里写入任意值。

4)整型变量溢出(integer variable overflow)

整型变量溢出从造成溢出原因的角度来说可以分为三大类:存储溢出、计算溢出和符号问题,大多数整形溢出不能直接利用,但如果该整形变量决定内存分配等操作,就有可能间接利用该漏洞。下面简单谈谈存储溢出。

存储溢出就是使用不同的数据类型来存储整型数造成的。例如下面程序所示:

int len1 = 0x10101;

short len2 = len1;

由于len1和len2的数据类型长度不一样,len1长度32位,而len2长度16位,进行赋值操作后,len2无法容纳len1的全部位,导致了与预期不一致的结果,即len2等于0x0101。

而且,把短类型变量赋给长类型变量同样存在问题,例如如下代码:

short len2 = 0x0101;

int len1 = len2;

上面代码的执行结果并非总是如预期的那样使len1等于0x0101,在很多编译器编译的程序中结果是使len1等于0xffff0101,实际上就是一个负数。这是因为当len1的初始值等于0xffffffff,而把short类型的len2赋值给len1时只能覆盖掉其低16位,这就造成了安全隐患。

5)其他的攻击手法(others)

利用ELF文件格式的特性如:覆盖plt(过程连接表)、dtor(析构函数指针)、got(全局偏移表)、return-to-libc(返回库函数)等的方式进行攻击。

3 缓冲区溢出攻击的防御方法

通过分析缓冲区溢出攻击的原理,可以总结出缓冲区溢出的几个条件:

1) 程序编译时在堆栈上分配了固定大小的缓冲区,并且在对缓冲区进行访问时没有提供边界检查。

2) 程序调用了没有进行边界检查的函数来访问(写操作) 缓冲区,这些函数没有对访问的缓冲区的大小进行判断。

3) 返回地址存放在堆栈的底部 使得通过溢出可以覆盖返回地址

4) 堆栈的属性一般是可执行的 使得恶意代码可以执行

现阶段采用的防范缓冲区溢出攻击的手法很多,较为成熟的主要有以下几种。

1) 编译保护

因为缓冲区溢出的通常都会改写函数返回地址,这里可以产生一个值放在返回地址之前,假如当函数返回时候发现这个值被修改,就证明有可能被进行缓冲区溢出攻击,程序在记录到此修改之后立即响应,发送警告给管理员,并且立即终止进程。

另一种方法是创建一个堆栈来存储函数返回地址,并且在受保护的函数开头和结尾部分增加一段代码,开头处的代码用来将函数返回地址拷贝到一个特殊的表中,而结尾处的代码用来将返回地址从表中拷贝回堆栈。因此函数执行流程不会改变,将总是正确返回到主调函数中。当调用一个地址在非文本段内的函数指针时,将终止函数的执行。

2) 库函数链接保护

使用特殊的CPP(gcc预编译程序)宏取代原有的*printf()的参数统计方式,比较传递给*printf的参数的个数和格式串的个数,如果格式串的个数大于实际参数的个数,就判定为攻击行为,向管理员发送消息并终止进程。

3) 不可执行的栈和数据段

去掉堆栈和数据段的执行权限,例如跟踪一个应用程序所包含的可执行映像的最大虚拟地址,动态的维护这个“可执行虚拟地址的最大值”,每次发生进程切换的时候调度进程动态的跟踪每个应用程序,所以每个程序运行时都有不同的“可执行虚拟地址的最大值”,因为可执行限界通常是个很低的虚拟地址,所以除了stack以外mmap()映射的区域以及malloc()分配的空间都处在可执行限界之上,因此都是不可执行的。

除以上三种防御手段之外,很多防御软件针对于缓冲区溢出攻击特别设计了增强的缓冲区溢出保护方式及内核MAC,例如使用strlcpy()和strlcat()函数替换原有的危险函数、内存保护如mmap()随机映射、malloc()随机映射、特权分离、特权回收、基于x86段式内存管理的数据段不可执行、基于页式内存管理的数据段的页不可执行、内核页只读等。并且,现阶段出现的硬件级别的保护可以大大增加缓冲区溢出的防护。

4 结束语

缓冲区溢出是目前导致黑客横行的主要原因,比如冲击波,红色代码等都是利用缓冲区溢出漏洞的典型。缓冲区溢出是一个编程问题,防止利用缓冲区溢出发起的攻击,关键在于程序开发者在开发程序时仔细检查溢出情况,不允许数据溢出缓冲区。此外,用户要经常登录操作系统和应用程序提供商的网站,及时下载补丁程序,弥补系统漏洞。

参考文献:

[1] 谢恒建,胡兆阳.缓冲区溢出攻击的防护技术分析[J].微计算机信息,2005(17):11-13.

[2] 池瑞楠. Windows缓冲区溢出攻击的实例研究[J].微计算机信息,2007,(3):665-666.

[3] 高阳,罗军舟.基于灰色关联决策算法的信息安全风险评估方法[J].东南大学学报(自然科学版), 2009,(2):225-229 .

[4] 殷立峰. 缓冲区溢出攻击的工作原理及防范策略[J].计算机安全,2008 (3):83-85.

[5] 吕涛,曹天杰.基于内存地址确认的缓冲区溢出检测方法[J].微计算机信息,2009(36):68-70.

上一篇:浅谈局域网的安全管理与保障 下一篇:基于XML数据库与LINGO的高校绩效评价决策支持...