缓冲区溢出 - 用金丝雀防御不成功

Buffer Overflow - defending with canary not successful

我正在尝试使用金丝雀来保护程序,但某些输入仍然无法防御。 这是代码:

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

int urandom() {
    #ifdef __unix__
    int var;
    FILE *fd = fopen("/dev/urandom", "r");
    fread(&var, sizeof(int), 1, fd);
    fclose(fd);
    return var;
    #else
    return 4;
    #endif
}

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

int main(void) {
    int begin_canary = urandom();
    char buff[15];
    int pass = 0;
    int end_canary = begin_canary;

    printf("\n Enter the password : \n");
    gets(buff);

    if(strcmp(buff, "thegeekstuff"))
    {
        printf ("\n Wrong Password \n");
    }
    else
    {
        printf ("\n Correct Password \n");
        pass = 1;
    }

    if(pass)
    {
       /* Now Give root or admin rights to user*/
        printf ("\n Root privileges given to the user \n");
    }

    if (begin_canary != end_canary) {
        printf("Alert! Buffer Overflow detected.\n");
        exit(1);
    }

    return 0;
}

对于包含随机字符的输入:"alskdjasldkjasldkjaslkdlkajsd" 检测到缓冲区溢出并打印 "Alert!"。 但是出于某种原因,对于仅包含一个字符(例如 24 o)的输入,两个金丝雀具有相同的值,并且程序因分段错误而崩溃并且没有打印 "Alert!".

我的金丝雀机制有什么问题?

谢谢。

您的检查只验证相同的金丝雀,而不是值没有改变...如果您一遍又一遍地输入相同的字符,发生的事情是您已经覆盖了两个金丝雀具有相同的值,因此它们仍然匹配。

为了更有效,您需要根据 而非 存储在堆栈中的值进行验证,例如将其存储在全局变量中:

int check_canary;
int get_canary(void) {
    if (!check_canary) {
        checK_canary = urandom();
    }

    return check_canary;
}

/* ... */

int begin_canary = get_canary();
char buff[15];
int pass = 0;
int end_canary = get_canary();

然后对照 check_canary 检查:

if (begin_canary != get_canary() || end_canary != get_canary()) {
    /* fail */
}

一些值得一提的注意事项:

  • 上面只会为所有调用选择一个 canary 值,这可能没问题,因为它会在您每次执行程序时发生变化。
  • 实际上并不能保证堆栈会按照您声明变量的顺序进行布局,因此此检查是否真的有效取决于对编译器实现的许多假设。例如,如 chux 所述,您可以通过将其包装在结构中来解决这个问题。

编译器可以自由地重新排列您的局部变量。尝试添加以下诊断:

printf("%p\n%p\n%p\n", &begin_canary, &buff, &end_canary);

例如,我得到:

0xbf976168
0xbf976171
0xbf97616c

所以两个金丝雀值都低于堆栈上的缓冲区,在这种情况下堆栈保护根本不起作用。

我得到的两个答案都指导我解决了问题。

在输入由一个字母组成的情况下,两个金丝雀都 运行-over 具有相同的值。发生这种情况是因为编译器将它们一个接一个地放入堆栈中,而不是按照它们定义的顺序。 编译器做了这些优化,这证明金丝雀应该由它来实现。

解决方案是制作金丝雀 volatile 以防止优化。 可能还有其他解决方案。