什么是缓冲区溢出?
缓冲区溢出(buffer overflow),在计算机领域是一种异常现象。缓冲区溢出指当一段程序尝试把更多的数据放入一个缓冲区,数据超出了缓冲区本身的容量,导致数据溢出到被分配空间之外的内存空间,使得溢出的数据覆盖了其他内存空间的数据。攻击者可以利用缓冲区溢出修改计算机的内存,破坏或控制程序的执行,导致数据损坏、程序崩溃,甚至是恶意代码的执行。
缓冲区溢出攻击的类型
缓冲区溢出攻击指利用缓冲区溢出漏洞所进行的攻击行动。通过向程序的缓冲区写入超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,造成程序崩溃、系统关机或使程序执行其它指令,以达到攻击的目的。缓冲区溢出攻击有很多类型,主要可以分为以下几类:
- 栈溢出:栈溢出是指在程序执行中,如果在栈上分配的内存超过了栈的大小,就会发生栈溢出。栈是一种后进先出(LIFO)的数据结构,用于存储程序执行过程中的临时变量。当栈溢出时,程序会立即停止执行,并显示栈溢出错误信息。栈溢出攻击是最常见的缓冲区溢出攻击类型,发生栈溢出的基本前提是程序必须向栈上写入数据且写入的数据大小没有被控制。
- 堆溢出:堆溢出是指程序在动态分配内存时,分配的内存超出了堆的大小。堆是一种先进先出(FIFO)的数据结构,用于存储程序运行时长期需要的数据。当堆溢出时,程序可能不会立即停止执行,但会导致程序的不稳定,甚至崩溃。在恶意攻击中,攻击者可能会利用堆缓冲区溢出来执行任意代码或获取敏感信息。
- 格式字符串溢出:格式字符串指在编程语言中,涉及使用格式化字符串函数来打印字符串时,如果格式串由用户定制,攻击者就可以任意伪造格式串,利用 *printf() 系列函数的特性就可以窥探堆栈空间的内容,超长输入可以引发传统的缓冲区溢出,或是用“%n”覆盖指针、返回地址等。
- 整数溢出: 计算机语言中整数类型都有一个取值范围,两个整数进行运算时,若结果大于最大值(上溢)或小于最小值(下溢),就是溢出。例如,最大值为a,在最大值与最小值之间发生以下计算:a+1=0或0-1=a,此时会发生溢出,其中a+1=0会发生上溢,0-1=a会发生下溢。利用整数的范围和符号等问题触发安全漏洞,大多数整形溢出不能直接利用,但如果该整形变量决定内存分配等操作,作为漏洞被间接利用。
- Unicode 溢出:Unicode 溢出通过将 Unicode 字符插入需要 ASCII 字符的输入中来创建缓冲区溢出。ASCII 和 Unicode 是使计算机表达文本的编码标准。由于 unicode 中有更多可用的字符,所以许多 unicode 字符大于最大的 ASCII 字符。当出现Unicode 溢出,可改变程序的工作方式,出现进一步的安全问题。
攻击者如何利用缓冲区溢出
攻击者可以将精心制作的数据输入程序,程序尝试将该输入数据存储在缓冲区中,输入数据会覆盖连接到缓冲区空间的部分内存。如果程序的内存布局定义明确,则攻击者可故意覆盖已知包含可执行代码的区域,然后用自己的可执行代码替换这些代码,改变程序的工作方式。通常,缓冲区溢出攻击都是按照如下步骤进行:
- 注入攻击代码。
- 跳转到攻击代码。
- 执行攻击代码。
攻击者可利用的方法较多,下面介绍两种攻击者常用的缓冲区溢出攻击:
- 利用栈溢出攻击破坏栈数据
可能被攻击者利用缓冲区溢出漏洞进行破坏的对象包括栈数据中的ARG(函数调用时的实参)、RETADDR(下一条要执行的操作指令在内存中的地址)、EBP(调用该函数前的栈帧状态值)和LOCVAR(该函数中的本地变量)。
常见的栈溢出攻击的利用方式是改变RETADDR的值,将已经注入到栈中的攻击代码的地址或代码区中某些具有特权的系统函数地址存放至RETADDR。若完成修改RETADDR的值,结束调用该函数后,程序就跳转到攻击者设计好的地址去执行攻击者希望被执行的指令,进而获得系统控制权限,导致严重的后果。EBP也常常作为被攻击的对象。攻击者通过构建一个RETADDR指向攻击代码的虚拟栈帧,再溢出当前栈帧EBP的值,溢出后的EBP值是构造的虚拟栈帧的地址。最终通过构造的虚拟堆栈的承接,执行完当前栈帧则执行虚拟栈帧,执行完虚拟栈帧则跳转到虚拟栈帧的RETADDR值所指向的位置,也使得程序最终跳转到攻击者设计好的地址去执行攻击指令。
- 利用堆溢出攻击破坏堆数据
由于堆中是不连续地动态分配内存,攻击者预测地址的难度提高,堆溢出攻击比栈溢出攻击更困难,但依然有技术可以利用堆溢出实现攻击。
Dword Shoot攻击:Dword Shoot指能够向内存任意位置写入任意数据,1WORD=4个bytes,即通过执行程序将4bytes的数据写入4bytes地址中,从而实现某种恶意操作。Dword Shoot攻击指利用Dword Shoot进行的恶意操作。Linux系统和windows系统对堆的管理方式都是双向链表方式,每一个分配的内存块由3部分组成:头指针(head)、尾指针(tail)、内存数据(data)。针对堆内存的管理主要有分配和释放两部分。在释放堆内存M时,会将M从链表上摘除,会执行M→head→tail=M→tail操作,如果攻击者通过溢出M临近的内存,将M的头指针、尾指针修改,让M的头指针指向攻击者设计好的虚拟节点,让M的尾指针指向攻击者设计好的位置,比如Shellcode,那么当执行完M→head→tail=M→tail操作时,该虚拟节点的尾指针就指向Shellcode,调用该虚拟节点的尾指针就会转向Shellcode。摘链时的另一操作M→tail→head=M→head,利用同样的原理,也可实现攻击。
Heap Spray攻击:Heap Spray是在Shellcode前面加上大量的slide code(滑板指令,指不会影响程序执行,但占据内存空间,使真正的攻击指令得到执行的无意义指令),组成一个注入代码段。之后向系统申请大量内存,并且反复注入代码段来填充,然后结合其他的漏洞攻击技术控制程序流,使得程序跳转执行到堆上,使Shellcode得到执行,Shellcode中的核心攻击指令得到执行,进而获得系统控制权限,达到攻击目的。
如何防止缓冲区溢出攻击
缓冲区溢出攻击占了远程网络攻击的绝大多数,这种攻击可以使得一个匿名的Internet用户有机会获得一台主机的部分或全部的控制权。如果能有效地消除缓冲区溢出的漏洞,则很大一部分的安全威胁可以得到解决。有几种基本的方法保护缓冲区免受缓冲区溢出的攻击和影响,具体方法如下:
- 完整性检查:在程序指针失效前进行完整性检查。
- 随机化地址空间: 关键数据区的地址空间位置随机排列。通常,缓冲区溢出攻击需要知道可执行代码的位置,而随机化地址空间使这几乎不可能。
- 防止数据执行:标记内存的某些区域为可执行或不可执行,从而阻止在不可执行区域运行代码的攻击。
- 编写安全的代码:使用能够帮助识别不安全函数或错误的编译器,利用编译器的边界检查来实现缓冲区的保护。避免使用不进行缓冲区检查的函数(例如,在C语言中,用 fgets() 代替 gets())。使用内置保护的语言编写或在其代码中使用特殊的安全性程序,来预防缓冲区溢出漏洞。
尽管存在上述预防措施,但仍能发现新的缓冲区溢出漏洞。当发现新漏洞时,工程师需要修补受影响的软件,并确保该软件的用户可以及时获取补丁。
华为帮您如何抵御缓冲区溢出攻击?
针对日益复杂的网络环境,华为提供了丰富的安全解决方案和安全产品,能够帮助您及时发现安全风险,降低缓冲区溢出的影响。
- HiSec Insight安全态势感知系统
HiSec Insight通过启用内存页上禁止执行代码的能力,防止缓冲区溢出漏洞,增强系统的安全性。启用内存地址随机化机制,通过对堆、栈或者共享库映射等线性区布局的随机化,增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的。
- HiSecEngine USG6000E/6000F系列AI防火墙
HiSecEngine USG6000E/6000F系列AI防火墙可以助您实施严格的出入方向流量控制,防卫内网威胁与外网威胁。通过应用识别、入侵防御(IPS)、反病毒、URL过滤及邮件过滤等内容安全相关的功能,USG系列AI防火墙能够对缓冲区溢出攻击进行有效阻断,保证内网服务器和用户免受威胁的侵害。此外,通过特征库升级,HiSecEngine USG6000E/6000F系列AI防火墙能够第一时间获取最新威胁信息,准确检测并防御针对漏洞的攻击。
- 作者: 谢赛雨
- 最近更新: 2023-07-04
- 浏览次数: 7093
- 平均得分: