作者:sickn3ss
翻译:YoCo Smart
警告:文章中的操作风险过高,依照文章内容测试应该在虚拟机环境下
在阅读本文之前,最好是有一定的基础,原文作者给大家整理了一些教程。
其他参考资料: Linux Exploit开发教程 第一章 堆栈溢出 | Linux Exploit开发教程 第二章 ret2reg指令绕过ASLR堆栈溢出 | ASM基础 | GDB相关
在第二章中用gcc对对漏洞程序编译时,我们使用了flag-z的堆栈执行(exec stack)的方式,但是现在大多数操作系统在默认情况下是非执行堆栈(non-exec stack)的方式
当然了,上次的程序是在BT4 R2中搞出来的,这次咱们换Debian Squeeze啦
阅读本文仍然需要一些基础知识:
我们先弄明白什么是非执行堆栈(non-exec stack),很好理解,非执行堆栈是为避免堆或者堆栈的内存区域被植入恶意代码执行,当然也可以直接防止一部分内存被写入恶意代码。
换句话说,这是个防止缓冲区溢出的功能。
本文并非详解非执行堆栈,如果你想了解非执行堆栈具体的工作等,可以参见维基百科
既然我们无法执行甚至无法植入我们的恶意代码,那该怎么办呢?可以使用ret2libc(Return to libc)
这个技术绕过非执行堆栈保护
你现在应该觉得libc技术会很有用,不过,你真的知道什么是libc吗?
上一章缓冲区溢出代码结构是这样的,如果你还有印象的话
JUNK + NOP sled + SC (Shell code) + EIP (overwrite with a JMP/CALL instruction to a register that points in our JUNK/NOP sled)
可惜现在是非堆栈执行啦,上面的代码如果不是在堆栈执行的模式下,是没用的。
libc是怎么来的:不再用指令覆盖EIP,直接调用libc库中我们需要的函数覆盖
实际上你可以在任何位置返回代码,不过libc的方法很广泛,因为它直接和程序相连,并且是最有效的调用。
你应该已经差不多了解个大概了,我们下面一步一步演示这个技术
又双叒叕是一个溢出
#include <stdio.h> #include <string.h> void evil(char* input) { char buffer[500]; strcpy(buffer, input); //我是漏洞 :-P printf("Buffer stored!\n"); printf("Buffer is: %s\n\n",input); } int main(int argc, char** argv) { evil(argv[1]); return 0; }
上一章我们是在堆栈执行无保护的情况下使用gcc编译的,这次呢,我们不做处理,直接让他在非堆栈执行的默认模式下编译。
gcc -ggdb -fno-stack-protector -o vulnerable vulnerable.c
将漏洞程序快速的附到gdb然后在call evil这个地方设置断点,然后从evil函数ret的地方开始计算payload所需要的偏移。
断点已经放置好了,我们植进一些垃圾代码让程序去处理看看会发生什么
通过上面的测试,我们知道应该用8字节以上的数据进行覆盖,也就是说总共加起来需要516字节的代码
我们发送的垃圾数据为512字节,剩下的4个字节是libc函数填充的
继续回到gdb中看看不发送
maint info sections ALLOBJ
这个指令的情况下libc是否可用
你应该注意到了,没有任何的NULL字节,libc是可用的
记得我说过,需要516字节以上的偏移,还有一开始我就说过我们不用JMP/CALL指令来覆盖EIP,因为结果会变成segfault
我们要做的是用libc的函数覆盖EIP,然后通过调用不同的函数来传递所需要的参数。
这两个函数是我们要重点用到的:
system() 这个函数用于执行指令或者参数程序
exit() 显而易见,终止退出的函数
我们下一步要做的就是找出system()和exit()两个函数的地址,当然了,还要找到/bin/bash的地址,让它来作为system()执行的参数,这个就是EXP的骨架了:
JUNK * 512 + system()函数地址 + exit()函数地址 + /bin/bash地址
好了,试着找出我们需要的地址以便进一步完成EXP吧
(gdb) print sysem $7 = {<text variable, no debug info>} 0xb7ec6180 <system> (gdb)
从图中来看system()的地址应该没什么问题,继续
(gdb) print exit $9 = {<text variable, no debug info>} 0xb7ebc300 <system> (gdb)
可惜地址中包含一个null字节,所以应该没什么用。好在exit()不是必须的,缺了它也可以的~我们的selvs中要是发现有像这样exit()包含null字节的情况,我们就要重新找一个像exit+偏移这样类似的来代替,以便exp更好的运行
比方说。、、看一下这里:0xb7ebc304,是滴,有一个<exit+4>,这正是我们需要的
(gdb) x/s 0xb7ebc304 0xb7ebc304 <exit+4>; "\350\246w\376\377\201\303\353,\021" (gdb)
继续看/bin/basn
(gdb) x/4000s $esp
找到以后这样:
剩下的我们只要更改一下地址,以便system()函数获取/bin/bash参数