全部博文(2759)
分类: 网络与安全
2015-06-04 06:24:50
原文地址:【安全健行】(2):Linux漏洞基础 作者:windhawkgyang
时隔这么久,自己终于重新开始了安全之路,虽然每天的工作和研读的论文也都是安全领域的技术,但是自己心里真正的安全还是漏洞的研究。因此,自己决定业余来自学这部分,今天算是一个正式的开始吧!
今天来简单介绍下Linux漏洞,因为Linux给予了用户更大的自由度和操控性,因此更加适合进行安全研究,我们也从Linux开始。这部分主要分为以下小节:
栈是操作系统进程内存中特有的结构,当系统运行一个程序,程序执行所需要的信息、数据都会以进程的形式存放到内存中供随时读写,这时内存中程序代码的大致结构就像下面这个样子:
栈是一种特殊的数据结构,可以想象出餐馆叠在一起的盘子,每次放上一个新盘子,之前最上面的盘子就到了下面,新盘子成为了最顶上的盘子;而取盘子时,总是从上向下依次取出,这样的数据结构我们称之为先进后出(FILO),栈就是一种这样的结构。
栈的增长顺序,在任何一个系统中都是由高内存向低内存扩展,我能想到一个直观的理由是系统默认可以从低内存开始处理数据(不考虑大端存储的话)。
栈有两个基本的操作,一个是将数据/元素压入栈中,即PUSH操作;另一个则相反,是将栈顶的元素弹出,存储到寄存器中,即POP操作。
内存中每个进程在内存栈段中都有自己的栈,而且栈始终是由最高内存地址向最低内存地址反向增长的。关于栈有两个重要的寄存器,一个是扩展基地址指针EBP,它指向栈底,即较高地址;另一个是扩展栈顶指针ESP,指向栈的较低地址。
系统在实际运行程序时,栈的应用往往表现在函数调用。每个函数都会有自己的栈,因此当发生一次函数调用时,调用函数和被调用函数都要进行一些栈的“交接”。比如对于调用函数来说,需要做以下工作:
这样,调用函数将当前的运行状态完整保存到栈中,同时压入了被调用函数的参数,接下来,就是被调用函数的工作了,我们一般称之为“开场白”:
我在自己的虚拟机Ubuntu12.04上编写了一个简单的C程序,然后使用disassemble命令反汇编程序文件,可以看到清晰的“开场白”和“收场白”:
缓冲区,本身是系统用于临时存放数据的一块内存区域,我们经常会用到变量复制的操作,将一个缓冲区的数据复制到另一个缓冲区,但是遗憾地是并非所有的函数都执行了严格的缓冲区大小检查操作。因此,当向一个缓冲区中复制超过其大小的数据时,数据就会超出其边界,覆盖影响到其相邻的区域。
假设我们现在有一个具有两个参数的函数调用过程,我们通过此时缓冲区在内存中的结构来说明下缓冲区溢出的原理。
在被调用函数得到执行权之前,调用函数会讲被调用函数的参数逆序压进栈中,这里就是Temp1和Temp2,然后压栈保存当前的EIP指针,最后利用Call命令将被调用函数的地址设置为EIP开始执行。
被调用函数的栈从EBP开始,到ESP为止,中间可以是函数内部定义的局部变量。如果函数内部定义了一个变量name是一个10字节的数组,当将一个100字节的Temp2复制到name时无疑会出现数据的越界,比如覆盖到EBP,甚至到达EIP,而此时原先的EIP便遭到了破坏,造成了缓冲区溢出。
缓冲区溢出的结果一般会造成拒绝服务,但是这是一个相对比较好的结果,因为起码程序给出了提示,有些时候EIP会被攻击者控制并以用户级访问权限执行恶意代码。而第三种则是最糟糕的情况,也就是EIP被控制并在系统级或根级执行恶意代码,此时攻击者获得了系统最高权限。
本地缓冲区溢出漏洞攻击要比远程漏洞攻击容易些,因为能够访问系统内存空间,并且更容易调试攻击代码。
一般一个漏洞攻击程序由三个部分构成:
最后,我们将以上三个部分组合起来,就是一个缓冲区溢出攻击的框架:
一般Shellcode不会太大,但是也不会太小,如果我们发现存在漏洞的缓冲区很小,不足以存放上面的Shellcode,那该如何呢?
方法就是将Shellcode存放到环境变量的位置,然后缓冲区覆盖EIP指向Shellcode。
这样之所以可以成功,是因为所有Linux ELF文件在映射到内存中时会将最后的相对地址设为0xbfffffff。环境变量和参数存储在这个区域,这些数据的下面就是函数栈,结构如图:
这样,我们就可以继续将EIP指向Shellcode执行攻击。
PS:
好了,Linux漏洞的基础知识就介绍到这里了,一些内存、堆栈的知识可以参考我之前的文章。今天虽然讲漏洞,但是没有列出代码和实验演示,感兴趣地朋友可以去Metasploit的网站浏览更多信息,那里有许多攻击实验的分享。
Refer: Gray Hat Hacking: The Ethical Hacker's Handbook, Third Edition