您的位置:首页 > 运维架构 > Linux

64位Linux下的栈溢出

2016-03-29 14:51 253 查看
from:http://packetstormsecurity.com/files/download/127007/64bit-overflow.pdf

本文的目的是让大家学到64位缓冲区溢出的基础知识。 作者Mr.Un1k0d3r RingZer0 Team

摘要

0x01 x86和x86_64的区别
0x02 漏洞代码片段
0x03 触发溢出
0x04 控制RIP
0x05 跳入用户控制的缓冲区
0x06 执行shellcode
0x07 GDB vs 现实
0x08 结语

0x01 x86和x86_64的区别

第一个主要区别就是内存地址的大小。这没啥可惊奇的: 不过即便内存地址有64位长用户空间也只能使用前47位要牢记这点因为当你指定一个大于0x00007fffffffffff的地址时会抛出一个异常。那也就意味着0x4141414141414141会抛出异常而0x0000414141414141是安全的。当你在进行模糊测试或编写利用程序的时候我觉得这是个很巧妙的部分。

事实上还有很多其他的不同但是考虑到本文的目的不了解所有的差异也没关系。

0x02 漏洞代码片段

为了节省漏洞利用的时间我决定打印缓冲区指针地址。

你可以用gcc编译上述代码。

这样就一切妥当了。

0x03 触发溢出

首先我们来确认一下确实可以让这个进程崩溃。

好来确认一下我们控制的RIP指令指针



你可以通过stepi单步执行来过一遍程序流程译者应该用ni比较合适。 当过了strcpy调用(0x40066c)之后你会发现当前缓冲区指针指向0x7fffffffdc90而不是0x7fffffffdcd0这是gdb的环境变量和其他东西造成的。不过现在我们不关心之后会解决的。 重要的说明* 在之后的内容中当我提到leave指令时就是指的上面的地址0x400685。 最后这是strcpy过后的栈

接着主函数(main)中的leave指令把rsp指向0x7fffffffdd98。 栈就变成了这样子

好极了我们有SIGSEGV的时机去查看当前寄存器的值。

好了程序就这样结束了我们没能控制RIP为什么因为我们覆盖了太多位记得最大的地址是0x00007fffffffffff吧而我们尝试用0x4141414141414141去溢出了。

0x04 控制RIP

我们发现了个小问题不过只要是问题总有办法解决的我们可以用个小一点的缓冲区去溢出这样指向rsp的地址就会像0x0000414141414141一样了。 通过简单的数学运算就可以很轻松地算出我们缓冲区的大小。我们知道缓冲区开始于0x7fffffffdc90。Leave指令之后rsp将指向0x7fffffffdd98。

0x7fffffffdd98 - 0x7fffffffdc90 = 0x108 -> 十进制的264

知道了这些我们可以把溢出载荷修改成这样

"A" * 264 + "B" * 6

rsp指向的地址应该像0x0000424242424242一样正常了。那样就能控制RIP。

这次我们直接看调用leave指令后的状况。 这是leave指令执行后的栈

这是leave指令执行后寄存器的值

rsp指向0x7fffffffddb8而0x7fffffffddb8的内容就是0x0000424242424242。看来一切正常是时候执行ret指令了。

我们最终控制了rip

0x05 跳入用户控制的缓冲区

事实上这部分内容没什么特别的或者新的东西你只需要指向你控制的缓冲区开头。也就是第一个printf显示出来的值在这里是0x7fffffffdc90。通过gdb也可以很容易地重新获得这个值你只需在调用strcpy之后显示栈。

是时候更新我们的载荷了。新的载荷看起来像这样

"A" * 264 + "\x7f\xff\xff\xff\xdc\x90"[::-1]

因为是小端结构所以我们需要把内存地址反序。这就是python语句[::-1]所实现的。

确认下我们跳入正确的地址。

这是执行leave指令后的栈。如我们所知rsp指向0x7fffffffddb8。0x7fffffffddb8的内容是0x00007fffffffdc90。最后0x00007fffffffdc90指向我们控制的缓冲区。

(gdb) stepi

ret指令执行后rip指向0x7fffffffdc90这意味着我们跳入了正确的位置。

0x06 执行shellcode

在这个例子中我准备用个定制的shellcode去读/etc/passwd的内容。

接下来汇编这个文件然后提取shellcode。

这个shellcode长82字节。来构造最终的载荷吧。

原来的载荷

我们要保证一样的大小所以264 - 82 = 182

然后把shellcode接在开头

来把所有东西一块儿测试

如果一切正常你就会看到/etc/passwd的内容。要注意内存地址是可以变化的这样可能就和我这里的不同了。

0x07 GDB vs 现实

因为gdb会初始化一些变量和其他的东西所以如果你试着在gdb之外使用同样的利用脚本就会失败。不过在这个例子中我加了个对printf的调用来输出缓冲区指针。这样我们就可以很容易地找到正确的值并且在真实的环境中获得地址。

这是使用我们在gdb中找到的值的真实版本

很显然利用不成功。因为地址已经从0x7fffffffdc90变成了0x7fffffffdcf0。幸好有这点printf的输出我们只需用正确的值调整一下载荷。

换成正确的值之后利用一切正常。

0x08 结语

希望你们能喜欢这篇关于Linux下x86_64缓冲区溢出的文章,有很多关于x86溢出的文章了,但64位的溢出比较少见。

祝你们拿到好多好多shell!

感谢

原文地址: http://drops.wooyun.org/tips/2288
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: