MASTG-TEST-0229: 未启用栈保护
概述¶
此测试用例检查应用程序的主二进制文件或任何库是否在没有堆栈金丝雀的情况下编译,因此缺乏堆栈粉碎保护,这是一种常见的针对缓冲区溢出攻击的缓解技术。
此测试适用于所有二进制文件和库
- 对于像 Objective-C 或 C/C++ 这样的非内存安全语言来说尤其重要。
- 对于纯 Swift 应用程序,通常可以跳过检查堆栈金丝雀,因为 Swift 在设计上被认为是内存安全的,并且传统的解析技术无法检测 Swift 二进制文件中的堆栈金丝雀(请参阅此博客文章的“canary – exceptions”部分)。
要区分 Objective-C 和 Swift 二进制文件,您可以检查导入和链接的库。 检测 Objective-C 二进制文件非常简单,但检测纯 Swift 二进制文件更具挑战性,因为根据 Swift 版本和编译器设置,该二进制文件可能仍包含 Objective-C 符号或库。 有关更多详细信息,请参阅此博客文章的“identifying objc vs swift”部分。
步骤¶
- 提取应用程序并识别主二进制文件( 获取和提取应用程序)。
- 识别所有共享库( 获取共享库)。
- 在主二进制文件和每个共享库上运行 获取编译器提供的安全功能。
- 如果输出包含符号
__stack_chk_fail
,则表示已启用堆栈金丝雀。
观察¶
输出应包含主二进制文件和每个共享库的符号列表。
评估¶
如果任何二进制文件或库不是纯 Swift,但不包含指示堆栈金丝雀的方法(如 objc_autorelease
或 objc_retainAutorelease
),则测试用例失败。
注意: 检查 __stack_chk_fail
符号仅表明在应用程序的某个位置启用了堆栈粉碎保护。 虽然堆栈金丝雀通常针对整个二进制文件启用或禁用,但在某些情况下,可能只有应用程序的部分受到保护。 例如,如果应用程序开发人员静态链接启用了堆栈粉碎保护的库,但禁用了整个应用程序的堆栈粉碎保护。
如果要确保特定的安全关键方法受到充分保护,则需要对每个方法进行逆向工程并手动检查堆栈粉碎保护。
评估时,请注意可能存在预期的误报,对于这些误报,应将测试用例视为已通过。 要确定这些情况,它们需要手动审查原始源代码和使用的编译标志。
以下示例涵盖了一些可能遇到的误报情况
使用内存安全语言¶
Flutter 框架不使用堆栈金丝雀,因为 Dart 缓解缓冲区溢出的方式。
编译器优化¶
有时,由于库的大小和编译器应用的优化,库最初可能是在启用堆栈金丝雀的情况下编译的,但它们已被优化掉。 例如,某些 react native 应用程序就是这种情况。 它们使用 -fstack-protector-strong
构建,但在尝试在二进制文件中搜索 stack_chk_fail
时,找不到它。 在这种情况下,React Native 开发人员声明他们不会添加 -fstack-protector-all
,因为在他们看来,他们认为这样做会增加性能损失,而不会带来有效的安全收益。