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

时间:2015-04-27 15:41:48

标签: c buffer-overflow

我正在尝试使用金丝雀来保护程序,但是一些输入仍然超出我的防御范围。 这是代码:

#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”检测到缓冲区溢出并且“警报!”打印出来。 但由于某些原因,输入只包含一个字符(例如24个),两个金丝雀都具有相同的值,程序崩溃并出现Segmentation Fault而没有打印“Alert!”。

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

感谢。

3 个答案:

答案 0 :(得分:4)

你的支票只验证金丝雀的相同,而不是价值没有改变......如果你一遍又一遍地输入同一个角色,发生的事情是你用两个金丝雀覆盖了这两个金丝雀相同的值,因此它们仍然匹配。

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

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值,这可能很好,因为每次执行程序时它都会改变。
  • 实际上并没有保证堆栈按照你声明变量的顺序排列,所以这个检查是否真正有效取决于对编译器实现的许多假设。例如,你可以通过将它包装在结构中来解决这个问题。

答案 1 :(得分:1)

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

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

例如,我得到:

0xbf976168
0xbf976171
0xbf97616c

因此两个canary值都低于堆栈上的缓冲区,在这种情况下堆栈保护根本不起作用。

答案 2 :(得分:0)

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

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

解决方案是让金丝雀volatile阻止优化。 可能还有其他解决方案。