Linux 栈溢出学习笔记

Linux操作系统提供了一些机制来阻止栈溢出攻击方法对系统产生危害。

1. 内存地址随机化机制。在Ubuntu和其他基于linux内核的系统中,目前都采用内存地址随机化的机制来初始化堆栈,这将会使得猜测具体的内存地址变得十分困难。关闭内存地址随机化机制的方法是: sysctl –w kernel.randomize_va_space=0

2. 可执行程序的屏蔽保护机制。对于Federal系统,默认会执行可执行程序的屏蔽保护机制,该机制不允许执行存储在栈中的代码,这会使得缓冲区溢出攻击变得无效。Ubuntu系统中默认没有采用这种机制。关闭可执行程序的屏蔽保护机制的方法是: sysctl –w kernel.exec-shield=0

3. gcc编译器gs验证码机制。gcc编译器专门为防止缓冲区溢出而采取的保护措施,具体方法是gcc首先在缓冲区被写入之前在buf的结束地址之后、返回地址之前放入随机的gs验证码,并在缓冲区写入操作结束时检验该值。通常缓冲区溢出会从低地址到高地址覆写内存,所以如果要覆写返回地址,则需要覆写该gs验证码。这样就可以通过比较写入前和写入后gs验证码的数据,判断是否产生溢出。关闭gcc编译器gs验证码机制的方法是:在gcc编译时采用-fno-stack-protector选项。

 4. linux下开启了NX的以及windows下开启了DEP的程序堆栈是不可执行的, linux下的程序默认编译时是开启了NX的,故很久以前采用的通过jmp esp或者jmp rsp跳板技术跳转到栈中的shellcode执行的栈溢出攻击方式基本上已经失效。 ASLR,全称为 Address Space Layout Randomization,地址空间布局随机化。ASLR 技术在 2005 年的 kernel 2.6.12 中被引入到 Linux 系统,它将进程的某些内存空间地址进行随机化来增大入侵者预测目的地址的难度,从而降低进程被成功入侵的风险。

图为Linux系统的栈帧结构。经典的操作系统(如各种类UNIX操作系统)中,栈总是向下增长的,压栈(push)时栈顶地址减小,弹栈(pop)时栈顶地址增大。

通过一个简单的小例子来看Linux和Windows堆栈溢出条件对比:

接下来我们增加Name数组的长度,在Windows下改“abcdefghijklmnopqrstuvwx”,然后运行:

windows中将刚才得到的程序放入OD中调试:

仔细看显示的 “0x74737271”就是“qrst”,“q”是第17位字母,看来就是第17个数覆盖到了EIP。原因:Windows为Char output[12]分配了12字节的空间,后面是4字节的EBP,再后面就是4字节的EIP了。如果传递给Output的值超过了16个字节,那就会进入EIP,把保存的返回地址覆盖了,这样程序在返回的时候就会出错了。

在Linux下呢?我们先从a到x,居然没有反应?

我们继续加长,看它溢出不溢出!加到“yz12”的时候,终于出错了。看来Linux系统为Output分配的空间不仅仅是12个字节,要多得多。在填充了28个字符之后,达到了返回点的位置。

我们可以总结出:Linux和Windows堆栈溢出条件是相似的,都是字符串超过数组的大小而溢出的;产生溢出后,它们都会报错,可以方便我们定位溢出点。但它们也有些许不同,在Linux下,不像Windows恰好给数组分配定义大小的这么多,Linux下用GCC编译的时候会随机填充一定数目的无用数据。另一个区别是Windows报错时,会给出EIP当时的值,可以方便我们利用,而Linux只会提示段错误,不会报出错的数据。

在知道了返回点EIP的位置后,我们就可以覆盖返回点为任意值,让程序运行到一个错误的地方,从而出现报错提示。那如果把EIP覆盖成我们想去的程序的地方就可以运行我们想要的程序了(执行shellcode)

如果按照一般的方式编译,linux系统能够探测到程序中的stack  overflow,从而终止程序。

。为了禁用掉其他的保护(主要是编译器生成的运行时栈检测代码) 我们可以这样来编译我们的例子:

下面我们来通过GDB调试一个例子来具体分析。

利用gdb先对main函数进行调试,其次再观测寄存器的地址值以及rsp中的值。

继续si单步调试,持续观测rsp中的值,发现其按照先参数入栈,然后返回地址入栈,然后前栈桢的EBP入栈的方式,与X86类似

注意上图4005d1就为返回地址,而后面的值为传入的参数。

再接着单步执行,发现返回地址压入栈后,前栈桢的EBP入栈:

然后我们继续调试,单步执行,发现strcpy函数执行完毕以后,esp距离前栈桢ebp的距离大于128个字节,有144个字节,得知了字节数就容易构造shellcode从而执行shellcode,此时需要构造的shellcode的填充字节为144+8(8为前栈桢的EBP)才能够覆盖返回地址。

猜你喜欢

转载自blog.csdn.net/weixin_40602516/article/details/81124385