二进制漏洞-栈溢出
github地址:https://github.com/ylcangel/exploits/tree/master/stack_overflow
你可以用于学习,但不能用于商用(如出书、以盈利为目的的课程、文章等),转载需标明出处。
测试平台
系统:CentOS release 6.10 (Final)、32位
内核版本:Linux 2.6.32-754.10.1.el6.i686 i686 i386 GNU/Linux
gcc 版本: 4.4.7 20120313 (Red Hat 4.4.7-23) (GCC)
gdb版本:GNU gdb (GDB) Red Hat Enterprise Linux (7.2-92.el6)
libc版本:libc-2.12.so
漏洞原理
在对栈缓冲区进行写操作时(如memcpy),未对缓冲区大小进行判断,导致写入数据长度可能大于缓冲区长度。
通用利用方式
写入数据覆盖返回地址,使返回地址指向恶意代码起始地址。由于我是基于本地测试,也就是libc库的版本已知,而基于远程攻击或不同版本的libc库可能会存在差异。
漏洞测试程序
很明显代码在执行scanf时未对缓冲区大小进行判断,存在栈溢出漏洞。
注意如无特殊说明,本文的exp都是基于该源码编译的二进制实现的。
所有测试均在linux环境下进行
开启RELRO
在前面描述的漏洞攻击中曾多次引入了GOT覆盖方法,GOT覆盖之所以能成功是因为默认编译的应用程序的重定位表段对应数据区域是可写的(如got.plt),这与链接器和加载器的运行机制有关,默认情况下应用程序的导入函数只有在调用时才去执行加载(所谓的懒加载,非内联或显示通过dlxxx指定直接加载),如果让这样的数据区域属性变成只读将大大增加安全性。RELRO(read only relocation)是一种用于加强对 binary 数据段的保护的技术,大概实现由linker指定binary的一块经过dynamic linker处理过 relocation之后的区域为只读,设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。RELRO 分为 partial relro 和 full relro。
Partial RELRO
- 现在gcc 默认编译就是 partial relro
- some sections(.init_array .fini_array .jcr .dynamic .got) are marked as read-only after they have been initialized by the dynamic loader
- non-PLT GOT is read-only (.got)
- GOT is still writeable (.got.plt)
Full RELRO
- 拥有 Partial RELRO 的所有特性
- lazy resolution 是被禁止的,所有导入的符号都在 startup time 被解析
- bonus: the entire GOT is also (re)mapped as read-only or the .got.plt section is completely initialized with the final addresses of the target functions (Merge .got and .got.plt to one section .got). Moreover,since lazy resolution is not enabled, the GOT[1] and GOT[2] entries are not initialized. GOT[0] is a the address of the module’s DYNAMIC section. GOT[1] is the virtual load address of the link_map, GOT[2] is the address for the runtime resolver function。
开启RELRO
-z norelro /-z relro -z lazy /-z relro -z now (关闭 / 部分开启 / 完全开启)
不完全开启relro