我正在尝试使用金丝雀来保护程序,但是一些输入仍然超出我的防御范围。 这是代码:
#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!”。
我的金丝雀机制出了什么问题?
感谢。
答案 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 */
}
值得一提的一些警告:
答案 1 :(得分:1)
编译器可以自由重新排列本地变量。尝试添加以下诊断:
printf("%p\n%p\n%p\n", &begin_canary, &buff, &end_canary);
例如,我得到:
0xbf976168
0xbf976171
0xbf97616c
因此两个canary值都低于堆栈上的缓冲区,在这种情况下堆栈保护根本不起作用。
答案 2 :(得分:0)
我得到的两个答案都指导我解决问题。
如果输入由一个字母组成,则两个金丝雀都以相同的值运行。发生这种情况是因为编译器将它们一个接一个地放在堆栈中,而不是按照它们的定义顺序。 编译器进行了这些优化,这证明了金丝雀应该由它实现。
解决方案是让金丝雀volatile
阻止优化。
可能还有其他解决方案。