1. 基础知识
1.1 数组原理
数组是一段连续的内存存储空间,包含多个类型相同的元素。通过数组名可以在内存中找到对应的数组空间,并且可以通过数组名和索引来访问数组中的元素。
#include <stdio.h>int main(){int a[10];int i=0;printf("a’s address:%p\n", &a);for(i=0; i<10; i++){a[i] = i;printf("a[%d]’s address:%p\n", i, &a[i]);}return 0;
}
程序定义了一个包含10个元素的数组 a
,并依次打印出每个数组元素的地址。运行结果显示,各个元素之间的地址相差一个 int
类型数值的大小,即 0x4 个字节。比如,a[4]
的地址与 a[0]
的地址相差 0x10 个字节,即 4*sizeof(int)
的大小。由此确定数组元素寻址的方式为:数组地址+索引*数组元素大小。
1.2 相关 CVE
CVE-2014-0497 漏洞: 是在 Flash 解析 AS3 代码时出现的数组越界漏洞。此漏洞涉及 Ii32
函数的一个参数为数组索引值,该函数会将数组对应位置的值取到变量中,但边界检查存在问题,导致攻击者可以构造特殊的数值绕过边界检查。利用该漏洞,攻击者可以读取内存中其他区域的数据。
CVE-2016-7193 漏洞: 是 Microsoft Office Word 中的一个数组越界漏洞。该漏洞在 Word 解析 RTF 文件格式时会被触发,成功利用后,攻击者可以远程执行代码,从而完全控制受害者的主机。漏洞的根本原因在于某数组实际分配的大小为 0x20,而程序逻辑中对该数组的边界限制为 0x40,导致数组后面的 0x20 数据能够被写入和修改。在攻击者的精心构造下,该漏洞可用于在用户打开特定的 RTF 文件时执行恶意代码。
CVE-2014-0244 漏洞:又称 OpenSSL “心脏出血” 漏洞部分原因是由于数组越界造成的。攻击者通过伪造 length
值,使得程序在合法数据之后越界访问,读取到存储在合法数据后面的信息。
1.3 数组越界检测工具
数组越界的防范方法包括使用 Address Sanitizer,这是一种快速内存错误检测工具,由编译器检测模块和运行时库组成。它可以检测堆区越界、使用后释放(UAF)等内存错误,是 LLVM/Clang 编译器的一部分。编译时通过 -fsanitize=address
参数可以启用 Address Sanitizer。
对于 C 和 C++(尤其是 C)的安全性,主要依赖开发人员控制数组边界。因此,开发人员在编程时需要特别注意数组的边界访问,以防止越界问题。
2. 漏洞原理
C 和 C++ 不对数组进行边界检查,依赖程序员的谨慎和程序的健壮性。数组边界检查本身具有难度,因为数组越界的判定不仅依赖下标的值,还取决于指针的类型;对于指向数组的指针来说,如果程序中没有显式地标明数组长度,还需要验证计算出的地址是否位于数组范围内。此外,数组在程序运行期间可能会进行动态分配,导致长度发生变化。
这些情况使得边界检查将会给程序性能带来极大的负担。因此C和C++中并不能很好的防范数组越界漏洞。数组周边数据的非法读写