缓冲区溢出不应该发生(?)

时间:2013-12-23 17:18:40

标签: c++ c gdb buffer-overflow disassembly

我有以下程序

 1  #include <stdio.h>
 2  #include <stdlib.h>
 3  #include <string.h>
 4  
 5  int check_authentication(char *password){
 6  char password_buffer[16];
 7  int auth_flag =0;
 8  
 9  
10  strcpy(password_buffer, password);
11  
12  if(strcmp(password_buffer, "brillig" ) == 0 )
13  auth_flag = 1;
14  if(strcmp(password_buffer, "outgrabe") == 0)
15  auth_flag = 1;
16  
17  return auth_flag;
18  }
19  
20  int main(int argc, char *argv[]){
21  if (argc<2){
22  printf("Usage: %s <password>\n", argv[0]);
23  exit(0);
24  }
25  
26  if(check_authentication(argv[1])){
27      printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n");
28      printf("    Access Granted.\n");
29      printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n");
30      }
31  else {
32  printf("\n Access Denied. \n");
33  }
34  }

我正在运行它通过gdb提供30个字节的As ...我正在设置以下断点

(gdb) break 9
Breakpoint 1 at 0x80484c1: file auth_overflow2.c, line 9.
(gdb) break 16
Breakpoint 2 at 0x804850f: file auth_overflow2.c, line 16.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

到目前为止一切顺利。一切都按照它应该进行到下一个断点

Breakpoint 1, check_authentication (password=0xbffff6d2 'A' <repeats 30 times>)
at auth_overflow2.c:10
10  strcpy(password_buffer, password);
(gdb) x/s password_buffer 
 0xbffff484:    "\364\237\374\267\240\205\004\b\250\364\377\277\245",            <incomplete      sequence \352\267>
 (gdb) x/x &auth_flag
 0xbffff494:    0x00

现在我们看到以下信息:

变量auth_flag位于地址0xbffff494中,变量缓冲区位于地址0xbffff484中。由于var auth_flag的地址大于缓冲区的地址,并且堆栈朝向较低的地址增长,这意味着缓冲区变量中的额外(缓冲区溢出)字节将不会超过auth_flag。对吗?

但是gdb有不同的意见......

(gdb) cont
Continuing.

Breakpoint 2, check_authentication (
password=0xbf004141 <Address 0xbf004141 out of bounds>)
at auth_overflow2.c:17
17  return auth_flag;
(gdb) x/s password_buffer 
0xbffff484: 'A' <repeats 30 times>
(gdb) x/x &auth_flag
0xbffff494: 0x41

和......

(gdb) x/16xw &auth_flag
0xbffff494: 0x41414141  0x41414141  0x41414141  0xbf004141
0xbffff4a4: 0x00000000  0xbffff528  0xb7e8bbd6  0x00000002
0xbffff4b4: 0xbffff554  0xbffff560  0xb7fe1858  0xbffff510
0xbffff4c4: 0xffffffff  0xb7ffeff4  0x080482bc  0x00000001

我们看到auth_flag被这些0x41(= A)覆盖,尽管这个变量位于堆栈的较低位置。为什么会这样?

4 个答案:

答案 0 :(得分:5)

堆栈增长方向与超出缓冲区时额外字节的位置无关。来自strcpy的超出总是会进入更高的地址(除非到目前为止溢出到地址为0,这是非常不可能的)

答案 1 :(得分:1)

对象存储在内存中,从较低的地址到较高的地址。由于您无法保证参数密码所引用的字符串长度小于16,因此您的代码无效。 实际上在本地缓冲区中没有任何需要password_buffer。 该函数可以按以下方式编写

_Bool check_authentication( const char *password )
{
    return ( strcmp( password, "brillig" ) == 0 || strcmp( password, "outgrabe" ) == 0 );
}

您可以在函数实现中使用int类型而不是返回类型_Bool。无论如何,将返回1或0。

答案 2 :(得分:1)

编译器可以自由地重新排序变量堆栈,因此在这种情况下,它始终是int变量之前的char数组。这使得程序容易受到基于堆栈的缓冲区溢出的影响。

为了更改以下内容:

(gdb) x/s password_buffer 
0xbffff484: 'A' <repeats 30 times>
(gdb) x/x &auth_flag
0xbffff494: 0x41

如预期答案如下:

(gdb) x/s password_buffer 
0xbffff494: 'A' <repeats 30 times>
(gdb) x/x &auth_flag
0xbffff484: 0x00

我们只需在编译期间添加-fstack-protector-all参数,结果将如预期一样。反之亦然,也许您可​​以使用-O0-fno-stack-protector

回答:https://stackoverflow.com/a/21215205/3205268

答案 3 :(得分:0)

如果您正在读取超过15个字节,那么您将获得该字节。 strcpy将查找字符串的结尾。您可以使用strncpy之类的东西来复制有限数量的字符。