tag 标签: 堆栈粉碎攻击

相关博文
  • 热度 10
    2022-5-31 18:48
    1166 次阅读|
    0 个评论
    在IAR Embedded Workbench开发工具中如何实现堆栈保护来提高代码的安全性
    作者: IAR Systems 随着越来越多的嵌入式产品连接到外部网络,嵌入式产品的信息安全性( Security )越来越多地被人们关注。其中既包括直接连接到外部网络,比如通过 Wi-Fi 连接;也包括间接连接到外部网络,比如汽车中的 ECU 通过 CAN 总线与 T-box 相连,而 T-box 通过移动网络可以连接到外部网络。特别是对于一些高功能安全性( Safety )要求的产品,如工业,汽车,医疗产品等,信息安全成为了功能安全的前提( There Is No Safety Without Security )。 在 C/C++ 中,堆栈缓存溢出( Stack Buffer Overflow )是一种常见的错误:当程序往堆栈缓存( Stack Buffer )写数据时,由于堆栈缓存通常采用固定长度,如果需要写的数据长度超过堆栈缓存的长度时,就会造成堆栈缓存溢出。堆栈缓存溢出会覆盖堆栈缓存临近的堆栈数据,其中可能包含函数的返回地址,就会造成函数返回时异常。如果堆栈缓存溢出是攻击者利用代码的漏洞蓄意造成的,它就称为堆栈粉碎( Stack Smashing )。堆栈粉碎是常用的一种攻击手段。 堆栈金丝雀( Stack Canaries ) , 因其类似于在煤矿中使用金丝雀来感测瓦斯等气体而得名,它可以用于在函数返回之前检测堆栈缓存溢出来实现 堆栈保护 (Stack Protection) ,从而提高代码的安全性。 相对于很多更加关注发挥器件性能的原厂开发工具,一些在行业中被广泛使用的商用开发工具更加关注性能和安全性的平衡性和完整性。 本文 以过去数十年来在行业中被广泛采用的商用工具链 IAR Embedded Workbench 为例,介绍如何在工具 中实现堆栈保护 ,从而提高代码的安全性 。 堆栈粉碎 在 C /C++ 中,堆栈( S tack ) 用于保存程序正常运行(比如函数调用或者中断抢占)的临时数据,可能包含如下数据: · 没有存储在寄存器中的函数参数和局部变量 · 没有存储在寄存器中的函数返回值和函数返回地址 · CPU 和寄存器状态 由于堆栈保存的是保证程序正常运行的临时数据,堆栈缓存溢出会覆盖堆栈缓存临近的堆栈数据,这些数据可能包含函数的返回地址,如果发生时一般会造成程序运行异常。攻击者经常利用这一点来进行堆栈粉碎攻击。 下面通过一个简单的例子来说明堆栈粉碎攻击: void foo(char *bar) { char c ; strcpy(c, bar); // no bounds checking } foo() 函数将函数参数输入复制到本地堆栈变量 c 。如下图 B 所示:当函数参数输入小于 12 个字符时, foo() 函数会正常工作。 如下图 C 所示: 当函数参数输入大于 11 个字符时, foo() 函数会覆盖本地堆栈的数据,将函数返回地址覆盖为 0x80C03508 ,当 foo() 函数返回时,会执行地址 0x80C03508 对应的代码 A ,代码 A 有可能包含攻击者提供的 shell 代码,从而使攻击者获得操作权限。 A 数据复制前 B "hello" 作为函数参数输入 C "AAAAAAAAAAAAAAAAAAAA\x08\x35\xC0\x80" 作为函数参数输入 图:堆栈粉碎示例 _ 堆栈保护 因其功能类似于在煤矿中用来发现瓦斯的金丝雀而得名的堆栈金丝雀( Stack Canaries ),可以用于在函数返回执行恶意代码之前检测堆栈缓存溢出。其检测原理是:当调用函数时,将需要保存的临时数据保存到堆栈,然后放置一个堆栈金丝雀 , 当函数返回时,检查堆栈金丝雀的值是否发生改变;如果发生改变,说明堆栈已被篡改,否则说明堆栈没有被篡改。 下面介绍如何在 IAR Embedded Workbench 这种广受欢迎的商用工具链中实现堆栈保护,从而提高代码的安全性: 在 IAR Embedded Workbench 中,会使用启发模式( Heuristic )来决定函数是否需要堆栈保护 : 如果函数局部变量包含数组类型或者结构体成员包含数组类型,或者局部变量的地址在该函数外被使用,该函数需要堆栈保护。 IAR Embedded Workbench 安装目录下面 \src\lib\runtime 包含 stack_protection.c ,里面包含了 __stack_chk_guard 变量和 __stack_chk_fail 函数,可以作为模板使用:其中 __stack_chk_guard 变量就是堆栈金丝雀的值,在函数返回时,如果检测到堆栈金丝雀的值被篡改,就会调用 __stack_chk_fail 函数。 1. 将 IAR Embedded Workbench 安装目录下面 \src\lib\runtime 文件夹的 stack_protection.c 拷贝并添加到工程。 2. 在 IAR Embedded Workbench 中启用堆栈保护。 3. 在代码中声明堆栈保护相关的 __stack_chk_guard 变量和 __stack_chk_fail 函数。 extern uint32_t __stack_chk_guard; __interwork __nounwind __noreturn void __stack_chk_fail(void); 4. 编译工程。编译器会在需要堆栈保护的函数中添加如下操作:在函数入口处先入栈( Push ),然后再额外保存堆栈金丝雀,具体的值用户可以在 stack_protection.c 中 更改 __stack_chk_guard ; 在函数出口,会检测堆栈金丝雀的值是否还是 __stack_chk_guard ,如果不是,说明堆栈被篡改,会调用 __stack_chk_fail 函数。 调试 将断点打到需要堆栈保护的函数反汇编( Disassembly )入口,暂停后发现编译器在函数入口处入栈操作之后额外将堆栈金丝雀保存: 在函数出口处打断点,然后运行程序,在函数返回时,会先检测堆栈金丝雀的值是否还是 __stack_chk_guard ,如果不是,说明堆栈被篡改,会调用 __stack_chk_fail 函数。 改变堆栈金丝雀的值使之与 __stack_chk_guard 不一致,然后运行程序,函数返回时将会调用 __stack_chk_fail 函数: 总结 本文主要介绍了堆栈粉碎 攻击如何利用堆栈缓存溢出来影响代码的安全性。通过在 IAR E mbedded Workbench 中实现堆栈保护可以检测堆栈的完整性,从而提高代码的安全性。 参考文献: 1. https://en.wikipedia.org/wiki/Stack_buffer_overflow 2. https://cwe.mitre.org/data/definitions/121.html 3. https://en.wikipedia.org/wiki/Buffer_overflow_protection 4. https://www.iar.com/knowledge/learn/programming/stack-protection-in-iar-embedded-workbench/ 5. IAR C/C++ Development Guide ( Stack protection )