修改gcc的补丁认证程序

Patch authentication program with gcc modification

我的问题是使用较新版本的 gcc 无法修复以下小身份验证程序的安全漏洞。

如果输入正确的密码,下面的程序应该只输出 "Access garanted",但是缓冲区溢出可能会 "hack" 这个程序。

程序(原始):

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

int check_auth(char *passwd){
    int auth_flag = 0;
    char passwd_buffer[16];
    strcpy(passwd_buffer, passwd);
    if(strcmp(passwd_buffer, "brillig") == 0){
        auth_flag = 1;
    }
    if(strcmp(passwd_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_auth(argv[1])){
        printf("=_=_=_=_=_=_=_=_=_=\n");
        printf("Access garanted!\n");
        printf("=_=_=_=_=_=_=_=_=_=\n");
    }else{
        printf("=_=_=_=_=_=_=_=_=_=\n");
        printf("Access denied!\n");
        printf("=_=_=_=_=_=_=_=_=_=\n");
    }
}

输出:

[w4r10ck@localhost Hacking_with_C]$ gcc auth_buffer_overflow.c 
[w4r10ck@localhost Hacking_with_C]$ ./a.out "outgrabe"
=_=_=_=_=_=_=_=_=_=
Access garanted!
=_=_=_=_=_=_=_=_=_=
[w4r10ck@localhost Hacking_with_C]$ ./a.out "brillig"
=_=_=_=_=_=_=_=_=_=
Access garanted!
=_=_=_=_=_=_=_=_=_=
[w4r10ck@localhost Hacking_with_C]$ ./a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
=_=_=_=_=_=_=_=_=_=
Access garanted!
=_=_=_=_=_=_=_=_=_=

所以我知道这是唯一可能的,因为缓冲区溢出以及由此导致的堆栈中下一个变量的覆盖。在我的例子中,这个变量是 "auth_flag" 变量,并且由于这个变量的值不等于 0,因此给出了在 main() 函数中执行 if 语句的条件。因此,我试图操纵堆栈,以便 "auth_flag" 不能再被覆盖。

程序(已尝试解决问题):

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

int check_auth(char *passwd){
    char passwd_buffer[16];
    int auth_flag = 0;
    strcpy(passwd_buffer, passwd);
    if(strcmp(passwd_buffer, "brillig") == 0){
        auth_flag = 1;
    }
    if(strcmp(passwd_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_auth(argv[1])){
        printf("=_=_=_=_=_=_=_=_=_=\n");
        printf("Access garanted!\n");
        printf("=_=_=_=_=_=_=_=_=_=\n");
    }else{
        printf("=_=_=_=_=_=_=_=_=_=\n");
        printf("Access denied!\n");
        printf("=_=_=_=_=_=_=_=_=_=\n");
    }
}

编译后输出为:

[w4r10ck@localhost Hacking_with_C]$ ./a.out "brillig"
=_=_=_=_=_=_=_=_=_=
Access garanted!
=_=_=_=_=_=_=_=_=_=
[w4r10ck@localhost Hacking_with_C]$ ./a.out "outgrabe"
=_=_=_=_=_=_=_=_=_=
Access garanted!
=_=_=_=_=_=_=_=_=_=
[w4r10ck@localhost Hacking_with_C]$ ./a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa
=_=_=_=_=_=_=_=_=_=
Access garanted!
=_=_=_=_=_=_=_=_=_=

很明显,修改没有t work. But I已经读到,这是因为较新的 gcc 版本没有按照程序给出的顺序排列变量,而是像想要的那样。 是否有可能修改像旧版本一样工作的 gcc?

此行非常不安全,因为 passwd 字符串可以是任意长度。

strcpy(passwd_buffer, passwd);

为什么不用这个?

strncpy(passwd_buffer, passwd, sizeof(passwd_buffer)-1);
passwd_buffer[sizeof(passwd_buffer)-1]='[=11=]';

(https://en.cppreference.com/w/c/string/byte/strncpy)


除了这个特殊的溢出问题之外,变量只是一种抽象,用于命名算法中的值,以帮助程序员详细说明推理。
一旦使用优化编译器,变量 甚至可能不存在 .
很难弄清楚这一点,因为当要求 unoptimised 构建以使用调试器时,我们实际上要求编译器使变量存在以便在调试器中观察它们。
但是优化后的代码与未优化的代码有很大不同。
即使在未优化的模式下,语言标准中也没有指定变量的布局方式。
编译器可以选择不同于另一个的布局。