我开发了一个基于 Beancount 的账本托管服务 HostedBeans,欢迎大家来了解纯文本复式记账并试用我的服务。
查看源代码

笔记:缓冲区溢出

缓冲区溢出可以说是针对系统级软件最常见的攻击方式了,这类攻击可以通过操作系统或软件提供的API接口,或者通过文件和网络,发送一个特定的字节序列,最终改变被攻击程序的行为,甚至取得被攻击进程的权限。

计算机使用栈来维护函数的执行状态,如果函数在栈上分配了一段缓冲区,但向其写入数据时没有进行充分的长度检查,那么溢出的数据就会覆盖栈上之后的内存。因为栈是由高地址向低地址延伸的,即栈底在高地址,而数据写入是由低地址向高地址覆盖的,所以溢出的数据会向着栈底,即调用者的栈帧进行覆盖。

在调用者和被调用者的栈帧交界处,存在着一些相当敏感的数据,如函数的返回地址,通过覆盖这个值,可以将控制权移交到任意地址。

攻击者需要将返回地址指向到攻击者构造的恶意代码,因此攻击者必须知道当前栈帧的地址。在经典的情况下,同一个程序在相同架构,相同的操作系统中的栈地址是固定的,攻击者很容易通过几次尝试来确认这个值。

针对这个思路,较新版本的Linux和GCC使用了一种地址空间布局随机化的技术,同一个程序每次运行时,程序的各个部分,如代码段,数据段,堆和栈,动态链接库都会被装载到随机的地址。

但攻击者可以在真正的恶意代码前加入足够长的nop指令,nop指令不执行操作,只是一个空指令。然后随机地进行跳转,只要能够跳转到nop序列中的任意一条指令,都可以执行到后面的恶意代码。

较新版本的GCC中还加入了一种“栈保护者”的机制,GCC会在每个函数的栈帧开始处加入一个随机的哨兵值,并在函数返回时检查该哨兵值,如果哨兵值发生了变化,即表示出现了缓冲区溢出,程序会中断执行。

因为攻击者需要在栈上构造可执行的恶意代码,因此还有一种思路就是将栈所在页标记为不可执行,由硬件进行这个检查,使得攻击者在栈上的恶意代码根本无法执行,目前有部分CPU支持这项特征。

以上是缓冲区溢出攻击的基本原理,和三种操作系统级别的防御对策,这些防御措施对用户态的程序而言都是透明的,无需程序员额外关注,同时这些机制的效率损失也相当小。

撰写评论

如希望撰写评论,请发邮件至 jysperm@gmail.com 并注明文章标题,我会挑选对读者有价值的评论附加到文章末尾。

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过 Telegram Channel 订阅我的博客日志、产品和项目的动态:

王子亭的博客 @ Telegram


通过邮件订阅订阅我的博客日志、产品和项目的动态(历史邮件):

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2024-04-08.