在缓冲区溢出返回地址被不正确的地址覆盖但它仍然有效

时间:2015-05-28 11:30:22

标签: c assembly stack buffer-overflow

我试图让缓冲区溢出,这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int check_authentication(char *password) {

   char password_buffer[16];
   int auth_flag = 0;
   strcpy(password_buffer, password);
   if(strcmp(password_buffer, "brillig") == 0)
     auth_flag = 1;
   if(strcmp(password_buffer, "outgrabe") == 0)
     auth_flag = 1;

   return auth_flag; 

}




int main(int argc, char *argv[]) {

   if(argc < 2) {
      printf("Usage: %s <password>\n", argv[0]);
      exit(0);
    }
    if(check_authentication(argv[1])) {
      printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
      printf(" Access Granted.\n");
      printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
    } 

    else {
      printf("\nAccess Denied.\n");
    }
}

命令行提供的密码将与&#34; brilling&#34;进行比较。和&#34; outgrabe&#34;如果用户输入与其中任何一个匹配,则将授予访问权限,否则将被拒绝。据我所知,如果提供的密码将大于16,将覆盖返回地址,但是当我输入&#34; A&#34; 17它没有被覆盖。而不是auth_flag被覆盖并且是65(十六进制中的0x41,即A)。我无法弄清楚为什么变量被覆盖而不是返回地址。我正在用这个

进行编译
  

gcc -fno-stack-protector -z execstack -g -o test test.c

希望你们能提供帮助。感谢。

2 个答案:

答案 0 :(得分:3)

正如Sourav Ghosh所说,你的程序会调用未定义的行为。

实际上,这意味着您需要深入了解生成的代码以查看堆栈的使用方式。

由于这在很大程度上依赖于您的环境(机器,操作系统,编译器版本等),因此我们无法为您调试它。 在 my 环境中,authenticated标志存储在ebp-12中,而缓冲区存储在ebp-28中。 ebp-28传递给strcmp,这意味着溢出将导致authenticated被覆盖。 较长的字符串最终将粉碎返回地址:&#34; 1234567890123456000000000000&#34;在我的位置触发了一个段错误。

建议的起点可能是汇编为汇编:

gcc -fno-stack-protector -z execstack -g -S test.c 

生成test.s,它将显示堆栈的外观。 您还可以使用反汇编程序查看二进制文件(objdumpida pro),如果从源代码编译不是您能负担得起的工具; - )。

编辑:从您的pastebin,我收集的是:

显然有某种形式的堆栈保护:

 movl    %gs:20, %eax
 [...]
 movl    -12(%ebp), %edx
 xorl    %gs:20, %edx
 je      .L5
 call    __stack_chk_fail

如果-12(%ebp)的位置不再是%gs:20,则会调用__stack_chk_fail(例如,您的堆栈已损坏)。

作为旁注,如果想要绕过身份验证的真正的攻击者可以控制您的身份验证&#34;旗帜,他们甚至懒得尝试删除返回地址以获得它的美丽。

答案 1 :(得分:0)

一旦输入(password)字符串包含超过15(不是16,需要终止null)charcater,使用strcpy()将调用undefined behaviour

  • 在此之前(除非发生这种情况),你的程序没问题。
  • 在那之后,你无法预测任何事情

因此,在您的程序显示UB之后,您不能指望它以所需的方式运行。它可以做任何事情,每一个预期或意外。是否覆盖 auth_flag无法确定。

也许,值得一提的是,变量的内存分配不一定以任何特定的顺序(外观)进行。您的编译器可以按照它认为合适的任何顺序自由分配内存。