缓冲区溢出 - 用金丝雀防御不成功
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
以防止优化。
可能还有其他解决方案。
我正在尝试使用金丝雀来保护程序,但某些输入仍然无法防御。 这是代码:
#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
以防止优化。
可能还有其他解决方案。