避免缓冲区溢出
13.1 ID_bufferOverflow
缓冲区溢出是一种高度危险的安全漏洞,也是很多安全问题的根源。
“缓冲区(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 函数,造成敏感信息泄露。
关于避免缓冲区溢出的方法和防御措施,可参见本规则相关规则的进一步介绍。
相关
ID_insufficientBuffer
ID_improperNullTermination
ID_zeroLengthAllocation
ID_unsafeStringFunction
ID_dangerousFunction
ID_missingHardening
ID_arrayIndexOverflow