缓冲区溢出是一种高度危险的安全漏洞,也是很多安全问题的根源。

“缓冲区(buffer)”的本意是指内存等高速设备上的区域,程序在这种区域内接收或处理数据,之后再一并输出到网络或磁盘等低速环境,起到提高效率的作用,故称缓冲区。连续的内存区域均可称为缓冲区,如数组等。对缓冲区的越界读写称为“缓冲区溢出(buffer overflow)”。

缓冲区之外可能是其他对象的数据,也可能是函数返回地址、资源分配信息等重要数据,缓冲区溢出往往会造成严重后果,如:

导致程序崩溃,或产生其他形式的拒绝服务漏洞

使攻击者能够篡改程序的行为,窃取或损坏敏感信息

为恶意代码的执行提供可乘之机

助力攻击者获取非法权限,威胁系统安全

示例:

int buffer[10];

for (int i = 0; i <= 10; i++) { // Off-by-one error

buffer[i] = 0; // Overflow when ‘i’ is 10

}

例中循环变量 i 等于 10 时,buffer[i] = 0 便造成了缓冲区溢出,此为常见笔误,应将循环条件改为 i != 10。

又如:

int auth() {

char pswd[8];

gets(pswd); // May overflow

return !strcmp(pswd, "abc");

}

例中 auth 函数验证用户输入的密码,密码存于数组 pswd 中,gets 函数接收用户输入,但不检查输入长度,一旦输入超过 8 个字符就会造成缓冲区溢出。由于 gets 函数过于危险,已被 C11 标准弃用,但在自定义的缓冲区操作中,仍然需要对这种问题保持高度警惕。

如果按下列方式调用 auth 函数:

int main() {

int ret = auth();

if (!ret) {

return -1; // May be invalid under attack

}

show_secret();

}

在进程的实际内存布局中,pswd 数组之后可以是 auth 函数的返回地址,如果攻击者利用缓冲区溢出将其篡改,便可以扰乱程序的执行,甚至可以绕过用于判断的 if 语句,直接执行用于显示敏感信息的 show_secret 函数,造成敏感信息泄露。

关于避免缓冲区溢出的方法和防御措施,可参见本规则相关规则的进一步介绍。