【安全课堂】缓冲区溢出浅析之二
作者: 日期:2015年03月16日 阅:2,620

在《缓冲区浅析之一》里,以针对nMap扫描器的二进制封装为例,我们讨论了一个简单的堆栈缓冲区溢出漏洞。我们展示了动过手脚的命令行参数是如何在堆栈上触发用户数据溢出的。SYN二进制扫描器在将数据提交给堆栈上的固定长度缓冲区之前,并没有对用户提供的缓冲区长度进行检查。在Debugger里进行分析时,我们发现该程序试图访问我们指定的内存地址0xdeadbeef。

这种情况为什么会发生?在回答这个问题之前,很有必要了解一下堆栈调用的工作原理。

每当一个函数被调用,堆栈指针就向下移动到邻近的地址,同时加上调用来源的相关信息,当函数返回时,这一信息按理将被清除。此外,每个函数都带有开场声明信息,这部分信息将自动在堆栈里占用特定大小的空间,比如样例程序里的`char buf[64]`语句。

这意味着buf是一个指针,指向比存储的返回地址更低的位置,它将在`main()`返回时被从EIP寄存器里清除掉。使用`sprintf()`而不是其姊妹语句`snprintf()`,再把buf指针放到特定位置上,有可能使得程序彻底崩溃,甚至扰乱程序,让其接受新的指令。既然我们的目标是获取拥有者权限(suid root),该漏洞有可能作为提权攻击的手段,创建一个根命令解释程序。

如果要利用这个漏洞执行任意代码,应该从内存的可执行区域调用特定的机器指令,替换返回地址。这一系列指令被称为外壳代码(shellcode),其作用是建立一个壳(Shell)。接下来我会使用一段AlephOne的文章中提到的Shellcode,来全面展示黑客是如何利用这一漏洞展开攻击的。

现在我们已经解释了背景知识,接下来只需要看一些基本的入侵代码了。以下代码的作用步骤如下:将Shellcode压进栈里、锁定指令的开始地址、制造一个缓冲区来重复外壳代码所在的地址、最后,从动过手脚的缓冲区开始执行被入侵的程序。

从函数调用到`execle()这段代码的作用是利用环境变量payload[]开始执行SYN扫描。通过在代码第14行的BASH变量的已知开始地址0xbffffffa上减去Shellcode自身长度和被调用程序名的长度,可以算出Shellcode的注入地址。

得到的地址用来为命令行变量建立缓冲区,如17和18行所示。因为返回地址应该是4位格式的,接下来就可以简单地重复计算出返回地址,来看看EIP寄存器是不是完美地被目标数据所替代了。接下来只剩下编译和测试入侵效果了,如下所示。

如果你是关心此类攻击的开发者,建议你对自己的代码进行一些静态分析。通过安全软件分析,类似于strcpy、strcat、springtf、的这些语句对越界字符串做的种种手脚将无所遁形。

申明:本文系厂商投稿收录,所涉观点不代表安全牛立场!


相关文章