【发布时间】:2020-08-06 01:58:36
【问题描述】:
我正在阅读 Jon Erickson 的优秀著作“黑客:剥削的艺术”,并试图理解他对缓冲区溢出的阐述。这本书似乎有点过时了。在他的示例中,他运行的是 x86 linux,而我在 x64 上复制结果时遇到了麻烦(我确实知道近年来增加了更大的堆栈保护)。特别是我正在努力复制他的exploit_notesearch.c 程序。
在本书的开头,他演示了一个程序 notesearch.c,该程序运行 suid root 并在库包含和函数声明之后具有以下初始行:
int main(int argc, char *argv[]) {
int userid, printing=1, fd;
char searchstring[100];
if(argc>1)
strcpy(searchstring, argv[1]);
else
searchstring[0]=0;
...
现在,Erickson 稍后演示了该程序的一个漏洞利用程序,称为exploit_notesearch.c,其骨架是:
char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a"
"\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80";
int main(int argc, char *argv[]) {
unsigned int i, *ptr, ret, offset=170;
char *command, *buffer;
...
if(argc>1)
offset=atoi(argv[1]);
ret=(unsigned int) &i-offset;
...
system(command);
free(command);
}
我省略的区域只是将正确的数据复制到command,首先写入"./notesearch '",然后注入60个NOP字节,然后注入shellcode中保存的数据,然后填充分配的其余内存地址为ret,字符串以' 结尾。
据我了解,exploit的思路应该如下。在执行system(command) 行时,系统会将notesearch 的main 函数的新堆栈帧推入堆栈。这个堆栈帧的底部是EIP 在完成主函数后应该返回的地址,中间的某个地方是为searchstring 缓冲区分配的空间。 ret 旨在近似分配给searchstring 的空间的开始,我们用NOP 指令(作为一个软糖因素)、shellcode(在执行时打开一个根shell)和几十个副本覆盖它地址ret 以确保我们将覆盖EIP 的返回地址。系统正常执行main,但随后,不是返回到exploit_notesearch代码中的地址,而是返回到地址ret,并继续执行所需的shellcode。定义ret 背后的想法是,i 位于堆栈帧正上方的某个堆栈帧中,用于notesearch.c 的main 函数,因此searchstring 不应该离i 太远,并且因此,通过尝试不同的偏移量,我们应该能够找到一个有效的偏移量。 (NOP 雪橇意味着我们不必完全精确。)
我想我大部分都正确理解了这一点,但也有一些问题。主要的一点是,我对system() 工作原理的理解是基于猜测,因为 Erickson 没有详细说明它是如何工作的。为了了解发生了什么,我尝试重写此程序以与 x64 linux 兼容,并进行了以下更改:
- 用为 x64 编写的 shellcode 替换 Erickson 给出的 shellcode
- 将
i、ret和offset更改为无符号长整数并将for循环中i的增量更改为8,以考虑更大的内存地址 - 使用
-fno-stack-protector标志编译notesearch.c和exploit_notesearch.c
然而,这根本不起作用,所以为了调试,我在notesearch.c 添加了一行打印地址searchstring 和一行到exploit_notesearch.c 打印地址ret。运行./exploit_notesearch 几次后,我得到了奇怪的结果:
trial 1:
ret: 0x7ffdc21f25ce
searchstring: 0x7ffee3c209a0
trial 2:
ret: 0x7fff6115703e
searchstring: 0x7ffd1233afb0
trial 3:
ret: 0x7ffeab00781e
searchstring: 0x7fff3c8a8760
那么,这里发生了什么?似乎调用system() 会以非常不可预测的方式更改堆栈,有时将新堆栈帧放在旧堆栈帧的下方,有时将其放在上面。使用gdb 进行调试没有帮助,因为似乎system() 的整个调用被捆绑到一行call 0x555555554710 <system@plt> 中,这并没有提供任何洞察力。
所以,我的主要问题是:
- 用
system()调用的shell 命令如何与堆栈交互? - 这在 x64 linux 中与在 x86 中的做法是否不同,还是我真的误解了 Erickson 编写的代码?
- 有没有办法在编译时禁用 x64 linux 上的这些安全措施,以便我在学习时可以跟随 Erickson 的代码?
对冗长的问题表示歉意,并提前致谢。
编辑:根据下面 Jester 的建议,我已禁用 ASLR,现在程序可以正常运行。那么作为一个后续问题,是否有人对理解 ASLR 有任何参考?干杯!
【问题讨论】:
-
ret和searchstring处于不同的进程中。它们的地址没有任何关系,尤其是在堆栈被随机化的情况下。您可以尝试关闭 ASLR,但即使这样也可能会使堆栈随机化。 -
system()只是一个普通函数,它使用堆栈的方式与调用任何其他函数的方式相同。 -
@Jester 非常感谢;两个问题。首先,您有了解流程和 ASLR 的参考资料吗?我以前没有听说过这些条款。其次,这些保护措施是在 2008 年左右吗?如果不是,埃里克森的代码应该如何工作?再次感谢!
-
@Barmar 抱歉,澄清一下——我不是指
system()的堆栈帧在哪里,而是使用system()调用的shell 命令的堆栈帧在哪里 -
对不起 - 由于主题的性质和安全性的极端相关性,这些天的漏洞利用(我希望!)通常在任何信息公开之前就已修复。您可能不得不徘徊在互联网的阴暗角落,才有机会真正尝试利用漏洞。不,我不知道任何更暗的角落:-o
标签: c linux assembly stack-overflow buffer-overflow