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

buffer overflow that shouldnt happen (?)

本文关键字:溢出 缓冲区 不应该发生      更新时间:2023-10-16

我有以下程序

 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:    "36423737426724020504b250364377277245",            <incomplete      sequence 352267>
 (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) 覆盖,尽管这个变量在堆栈中的位置较低。为什么会这样?

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

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

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

代替返回类型_Bool,您可以在函数实现中使用 int 类型。在任何情况下,都将返回 1 或 0。

编译器

可以自由地对变量堆栈进行重新排序,因此在这种情况下,它总是在 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

如果你读取的字节超过15个字节,你会得到它。 strcpy 将查找字符串的末尾。 您可以使用像 strncpy 这样的东西来只复制有限数量的字符。