您的代码是否安全地防止缓冲区溢出漏洞的威胁?
由Ram Cherukuri和Anirban Gangopadhyay
缓冲区溢出已经困扰了C / C ++开发社区多年。虽然C语言授权开发人员直接通过指针访问内存,但它也打开了溢出问题的门。安全编码实践有助于开发人员避免缓冲区溢出到某种程度上(以性能成本),但有时缓冲区溢出可能是微妙的,复杂的是找到和解决。
通常,只有当缓冲区溢出进入生产环境并被发现为安全缺陷(或者更糟,被利用)时,人们才会意识到它是如何巧妙地引入代码的。还记得Heartbleed虫子在流行的openssl图书馆?最近的Cloudbleed Bug.或者是Heartbleed解释提供良好的问题示例。
什么是缓冲区溢出?
一般来说,当将数据写入已分配缓冲区的操作超过缓冲区边界时,就会发生缓冲区溢出。在这样做的过程中,操作访问它不应该访问的相邻内存位置。让我们来看一个例子:
#define maxsize 100。。char localbuf [maxsize]。。得到(localbuf)
这gets()
功能不允许您检查数据是否读取localbuf.
少于最大限度
字符。您依赖于数据的发送方保持在这个限制内。恶意用户很容易通过发送大于最大限度
字符和访问堆栈中的相邻区域。
缓冲区溢出有多严重?
缓冲区溢出的严重程度取决于存储在相邻内存区域中的内容。当您在代码中调用一个函数时,与该调用相关的所有数据都存储在调用堆栈的相邻区域中。
考虑这两种情况:
- 与溢出缓冲区相邻的区域存储另一个局部变量或函数参数
由于缓冲区溢出,变量值已损坏。您可以遇到损坏的值,导致代码中其他地方的运行时出错。损坏的值也可能导致静默不正确的行为 - 可以转义检测的静默。你最终有不安全和不可靠的代码。 - 紧邻溢出缓冲区的区域存储返回地址的功能
调用堆栈还存储被调用函数返回时程序跳转到的地址。由于缓冲区溢出,该地址已被破坏。攻击者可以足够精确地溢出您的缓冲区,因此您的程序不会返回到调用站点,而是跳转到恶意代码的位置。如果您的程序具有足够的特权,则这种恶意代码可以控制您的应用程序并导致被利用。
避免缓冲区溢出有多难?
你可能会说上面所示的问题很容易避免。在使用非安全函数之前,例如gets()
,检查缓冲区大小。或者,更好的是,完全不使用这些不安全的函数。
考虑另一个缓冲区溢出的例子,看看它有多微妙。这个代码片段来自libPNG图像解码器,包括Mozilla和一些版本的Internet Explorer (cf. exe)在内的几个应用程序都使用它。OWASP).
如果(!(png_ptr->mode & PNG_HAVE_PLTE)){/*应该是一个错误,但我们可以处理它*/ png_warning(png_ptr, "Missing PLTE before tRNS");} else if (length > (png_uint_32)png_ptr->num_palette) {png_warning(png_ptr, "错误的tRNS块长度");png_crc_finish (png_ptr、长度);返回;}……png_crc_read (png_ptr readbuf (png_size_t)长度);
首先浏览,以下检查:
长度> (png_uint_32) png_ptr - > num_palette
这正是在使用length时避免缓冲区溢出所需要的。然而,这里的问题是检查发生在其他的如果
块。这如果
块前其他的如果
块执行不相关的检查png_ptr - >模式
;如果之前的检查失败,则控制超出了If - else If
链,只是一个警告。对可变长度的第二次检查根本不执行。
换句话说,存在一个执行路径子集,尽管进行了检查,但仍可能发生缓冲区溢出。只有当您能够跟踪程序中的所有执行路径时,才能检测到如此微妙的问题。这正是Polyspace静态分析所做的。
如何使用多空间静态分析来避免缓冲区溢出?
有很多静态分析工具声称可以检查缓冲区溢出,它们使用不同的启发式方法或某种形式的数据流分析来完成这一任务。然而,这是一种不够的方法,因为安全性和安全性关键的系统不能承受部署的嵌入式软件中出现任何假阴性(例如,缓冲区溢出的错过实例)。
波尔盖斯®下载188bet金宝搏产品采取两步方法来应对这一挑战。这种方法与软件开发工作流的需求保持一致。
PolySpace Bug Finder™适用的快速形式方法,使开发人员能够遵守编码指南,这有助于避免缓冲区溢出。PolySpace Bug Finder提供各种验证程序,不仅可以识别缓冲区溢出问题,还可以导致缓冲区和利用缓冲区溢出漏洞的其他潜在构造。此早期和快速的反馈使开发团队能够解决此类问题,然后在将进一步下游传播到软件构建,保存测试和调试资源之前。
例如,如果您遵循CWE或CERT C等安全标准,您可以使用Polyspace Bug Finder来遵守这些规则。以下CWE id、CERT C规则和ISO 17961安全编码指南处理缓冲区溢出:
你也有MISRA C:2012指令4.14,它说,“应检查从外部来源收到的值的有效性。”
Polyspace Bug Finder,通过它各种跳棋,提供广泛的支持,用于检测和避免基于堆栈的金宝app缓冲区溢出。
- 数组访问越界:您可以使用索引来超出数组的大小。
int我;int fib [10];For (i = 0;我< 10;i++) {if (I < 2) fib[I] = 1;Else fib[i] = fib[i-1] + fib[i-2];} printf("The 10-th Fibonacci number is %i .\n", fib[i]);
- 指针访问越界:您可以使用分配给内存块的指针来访问该块之外的内存。
int arr [10];int * ptr = arr;for (int i = 0;我< = 9;+ +){ptr + +;* ptr =我;/* ptr out of bounds for i=9 */}
- 使用污染索引的数组访问:您可以从外部源获取变量,并使用它作为数组索引,而无需检查该值小于数组大小。
#定义最大容量100
int arr(最大尺寸);
int func(int num) {int localVar;/*不检查num的中间代码*/arr [num] = localvar;
} - 字符串操作中的目标缓冲区溢出:使用字符串操作函数,例如
Sprintf()
写入缓冲区太大的写字符串,您正在写入。字符缓冲区[20];char *fmt_string = "这是一个非常长的字符串,它不适合在缓冲区";sprintf(缓冲区,fmt_string);
- 错误的字符串格式说明符导致缓冲区溢出:当您使用诸如
SSCANF.
,您的字符串格式说明符表示字符串大小大于为字符串分配的存储区域。char buf [32];sscanf (str[1],“% 33 c”,buf);
尽管PolySpace Bug Finder分析,但少数微妙和复杂的缓冲区溢出缺陷可以留在源代码中,因为PolySpace Bug Finder不是一个声音工具(即,它可能有假否定)。这是PolySpace Code Prover™可以提供帮助的地方,因为它是一种基于抽象的诠释的正式语义分析工具,可以彻底验证动态运行时行为。
如何使用PolySpace代码先驱来避免缓冲区溢出?
在我们之前的示例中,尽管保持了长度,但缓冲区溢出可能潜在地通过一个控制路径才能蠕变。当这些路径依赖于运行时信息时,您遇到了更复杂的。
如果(!(png_ptr->mode & PNG_HAVE_PLTE)){/*应该是一个错误,但我们可以处理它*/ png_warning(png_ptr, "Missing PLTE before tRNS");} else if (length > (png_uint_32)png_ptr->num_palette) {png_warning(png_ptr, "错误的tRNS块长度");png_crc_finish (png_ptr、长度);返回;}……png_crc_read (png_ptr readbuf (png_size_t)长度);
PolySpace代码先驱可以识别可变长度不受所有路径的限制。相应的PolySpace Bug Finder Checkers,数组访问越界和指针访问范围在美国,也要寻找这样的问题。当发现至少一个导致溢出的控制路径时,它们会报告。然而,这些检查程序并不执行详尽的分析。您必须使用代码验证工具,如Polyspace code Prover,并确保所有指针访问都是绿色的,因此不会证明会导致缓冲区溢出。
这样的详细分析是在代码检查和单元测试之前的理想选择,因为您可以基于Polyspace code Prover提供的运行时数据减少和优化您的测试用例。因此,同时使用Polyspace Bug Finder和Polyspace Code Prover,可以确保安全性,防止缓冲区溢出的恶性威胁。