探讨主流内存保护机制下的缓冲区溢出攻击可行形式
void foo(char *data){
char name[10];
printf("%p\n%p\n%p\n%p\n");//打印当前程序栈内存
strcpy(name, data);//覆盖返回地址
}
void hack(void){
puts("another place");
}
int main(){
char buf[10];
char buf2[10];
scanf("%s", buf);
foo(buf);
return 0;
}
以上是经典的缓冲区溢出攻击,当buf中输入超过10(16)字节,将会覆盖buf2中的内容;如果写入更多字节数,可以改变寄存器中保存的地址,使得被调用函数执行完成后返回到另一个函数的入口地址。
问题:
当前保护机制下,只能覆盖buf2中内容,但是即便跳过很长的内存保护的区域依然找不到记录被调函数返回地址的位置。1.在linux下不采用任何内存保护方法编译源代码的方法?2.关于现在内存保护机制下缓冲区溢出攻击的可能形式。
Answers
Stack protector 的实现是在在栈上放一个canary随机值,在函数调用完成后,检查这个值是否原样,如果不是原样则证明栈被破坏了。
你的原代码是有问题的。如果你输入的东西溢出了缓冲区,那么它就会覆盖scanf的返回地址,那么就无法正确返回,也就不能调用foo了。
我猜你想要的是这样:
void foo(char *data){
scanf("%s", data);
printf("%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n");
}
int main(){
char buf[10];
char buf2[10];
foo(buf);
return 0;
}
有些常识需要知道,不是你输入超出20个字符长度就一定能将返回地址覆盖。局部变量在栈上的分配,并不是紧凑的,通常情况下都会进行align,并且按16字节对齐。
所以,如果你输入很多字符(超过64?),你是能得到segment fault的。
但你没有办法改正确返回地址,一个是因为ALSR,Address space layout randomization,另一个是因为canary本身是随机值,你也无法覆盖正确,最终逃不过检查。
所以,你只能搞出segmentfault的效果,却并不能覆盖返回地址还能让代码继续正常运行。
1.在linux下不采用任何内存保护方法编译源代码的方法?
GCC有一个-fno-stack-protector参数。默认情况下stack-protector是启用的,GCC检测到函数参数类型为char *,并且没有限定长度时,它就会在函数调用前,在栈上放一个canary。
问题2目前我不想多去思考。