许多程序员在编程时都没有遵循一条规则,即操作字符串时,边界检查是必需的。否则,就有可能出现堆栈缓冲区溢出。
不管你是一个开发人员,还是测试或研究人员,理解缓冲区溢出的原理都很重要。尽管操作系统和编译器已经提升了安全性,但最好的安全还是自己能够识别并消除这种潜在的编程错误。
本文将通过一个编程例子,简要分析堆栈缓冲区溢出的原理和利用。下面是一段对特定目标进行Nmap TCP SYN扫描的源代码:
当编译后,这段代码会接受一个命令行参数,然后把它传递给 /usr/bin/nmap作为扫描目标。源代码编译后的二进制文件在此目录中。
键 入“ls”命令,可以看到root用户拥有的/usr/bin/synscan文件处于suid和sgid用户组中,也就意味着synscan的执行环境 得到root权限的许可,因此SYN扫描可以由任何授权用户发起。(注:直接给nmap设置黏着位能够允许所有用户以root权限执行任意命令)
不幸的是,对于管理员来说,该程序逻辑中有一个致命的漏洞,能够被利用获得root权限的shell。只需一点堆栈的知识就能发现,图1中程序的第14行可 以被利用。sprintf直接拷贝用户提供的主机名到堆栈缓冲区,这是极端危险的。目的缓冲区有64个分配的字节,但并没有进行边界检查以核实 argv[1]适合64字节的缓冲区。
原因在于,C语言把内存管理的权力留给用户。C语言的强大在于此,同样危险也在于此。