嵌入式操作系统
堆栈溢出对于我们软件开发人员来说,最严重的后果是破坏了内存中的指针,及其造成的一系列难查的bug。
在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。而缓冲区溢出中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。我在这里演示一下堆栈溢出的原理。
首先,介绍一下,与堆栈有关的一些概念:动态内存有两种,堆栈(stack),堆(heap)。堆栈在内存上端,堆在内存下端,当程序执行时,堆栈向下朝堆增长,堆向上朝堆栈增长。通常,局部变量,返回地址,函数的参数,是放在堆栈里面的。
低地址
局部变量
旧的基指针
返回地址
函数的参数(左)
函数的参数(。。。)
函数的参数(右)
高地址
我们可以写一个小程序测试:
Copycode
#include“string.h”
voidtest(char*a);
intmain(intargc,char*argv[])
{
chara[]=“hello”;
test(a);
return0;
}
voidtest(char*a)
{
char*j;
charbuf[5];
strcpy(buf,a);
printf(“&main=%p\n”,&main);
printf(“&buf=%p\n”,&buf);
printf(“&a=%p\n”,&a);
printf(“&test=%p\n”,&test);
for(j=buf-8;j《((char*)&a)+8;j++)
printf(“%p:0x%x\n”,j,*(unsignedchar*)j);
}
Main定义一个字符串hello,然后调用test函数,在test函数中,有一个长度为5的局部字符串变量buf,然后把复制参数a复制到buf中,这里因为没有a的长度小于等于buf的长度,所以并没有溢出buf。然后显示各个函数,参数,局部变量的地址,以及局部字符串变量buf和参数a之间的地址,我们看到:
Quote:
&main=0040100A
&buf=0012FF14
&a=0012FF28
&test=00401005
0012FF0C:0xcc
0012FF0D:0xcc
0012FF0E:0xcc
0012FF0F:0xcc
0012FF10:0xcc
0012FF11:0xcc
0012FF12:0xcc
0012FF13:0xcc
0012FF14:0x68 h 这里就是buf了!
0012FF15:0x65 e
0012FF16:0x6c l
0012FF17:0x6c l
0012FF18:0x6f o
0012FF19:0x0 \0
0012FF1A:0xcc
0012FF1B:0xcc
0012FF1C:0x1c 这里是
0012FF1D:0xff 两个
0012FF1E:0x12 旧的
0012FF1F:0x0 基指针,不管他
0012FF20:0x80
0012FF21:0xff
0012FF22:0x12
0012FF23:0x0
0012FF24:0x34 这个就是
0012FF25:0xb8 返回地址了
0012FF26:0x40 和main的地址很
0012FF27:0x0 接近吧!
0012FF28:0x78 这个是
0012FF29:0xff 参数a,即
0012FF2A:0x12
a字符串的
0012FF2B:0x0 地址
0012FF2C:0xe
0012FF2D:0x0
0012FF2E:0x0
0012FF2F:0x0
由于c编译器不会自己做边界检查的,所以,如果buf中的内容足够长,而不是hello,那么很有可能覆盖掉原来的返回地址,那么程序就会跳转到其他地方,为了试验,我们定义一个简单的函数echo,让test返回的时候跳转到echo。
用printf(“&echo=%p\n”,&echo);我已经知道了echo的地址是0x0040100f,从上面的例子已经知道从0012ff14到0012ff27要覆盖多少数据,数一下就知道了改写如下:
Copycode
#include“string.h”
voidtest(char*a);
voidecho();
intmain(intargc,char*argv[])
{
chara[16];
inti;
for(i=0;i《16;i++)a[i]=‘x’; //覆盖不重要的部分,为了达到返回地址
a[16]=0xf; //在这里改写了返回地址
a[17]=0x10;
a[18]=0x40;
a[19]=0x00; //一方面高字节正好是00,同时00又是字符串的结尾
test(a);
return0;
}
voidtest(char*a)
{
char*j;
charbuf[5];
strcpy(buf,a); //分配的缓冲区只有5,结果却有19,溢出了!
printf(“&main=%p\n”,&main);
printf(“&buf=%p\n”,&buf);
printf(“&a=%p\n”,&a);
printf(“&echo=%p\n”,&echo);
printf(“&test=%p\n”,&test);
for(j=buf-8;j《((char*)&a)+8;j++)
printf(“%p:0x%x\n”,j,*(unsignedchar*)j);
}
voidecho()
{
printf(“haha!\n”);
printf(“haha!\n”);
printf(“haha!\n”);
printf(“haha!\n”);
}
结果,我们看到地址显示完以后,出现了echo函数里面的haha!\nhaha!\nhaha!\nhaha!\n,说明溢出跳转成功,但是,在结束的时候出现了程序崩溃,这是因为echo找不到它的返回地址导致的。但是今天我们的目的,利用溢出执行其他代码的任务已经实现了
1988年,世界上第一个缓冲区溢出攻击——Morris蠕虫在互联网上泛滥,短短一夜的时间全世界6000多台网络服务器瘫痪或半瘫痪,不计其数的数据和资料被毁。造成一场损失近亿美元的空前大劫难!
计算机程序一般都会使用到一些内存,这些内存或是程序内部使用,或是存放用户的输入数据,这样的内存一般称作缓冲区。
溢出是指盛放的东西超出容器容量而溢出来了,在计算机程序中,就是数据使用到了被分配内存空间之外的内存空间。
缓冲区溢出简单的说就是计算机对接收的输入数据没有进行有效的检测(理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符),向缓冲区内填充数据时超过了缓冲区本身的容量,而导致数据溢出到被分配空间之外的内存空间,使得溢出的数据覆盖了其他内存空间的数据。
缓冲区溢出是一种非常危险的漏洞,在各种操作系统、应用软件中广泛存在。利用缓冲区溢出攻击,可以导致程序运行失败、系统宕机、重新启动等后果。更为严重的是,可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。
缓冲区溢出就好比一个杯子倒太多的水,洒出来,这叫溢出。它是一种非常普遍、非常危险的漏洞,最常出现在C/C++编写的程序中。
早期,人们还不那么重视数据安全的时候,往往会使用像strcpy这类不安全函数,这种函数对用户的输入不作任何检查,总之,来自不拒,那么,分配的内存空间是有限的,如果输入超长的字符串,必然会导致溢出。
讲了这么多,小伙伴会不会有点晕吗?其实,缓冲区溢出很容易理解的。下面我们看一个具体的例子:
这段代码存在一个经典的缓冲区溢出漏洞strcpy,为str变量分配了8个字节的空间,输入小于等于8个字符,那没问题:
但是当我们输入超长的数据时,问题出现了
在计算机安全领域,缓冲区溢出就好比给自己的程序开了个后门,这种安全隐患是致命的。缓冲区溢出在各种操作系统、应用软件中广泛存在。而利用缓冲区溢出漏洞实施的攻击就是缓冲区溢出攻击。
缓冲区溢出攻击,可以导致程序运行失败、系统关机、重新启动,或者执行攻击者的指令,比如非法提升权限。假如问题出现在常用的一些软件,比如操作系统、浏览器、通讯软件等,那后果不堪想象。
缓冲区溢出中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到系统权限,然后为所欲为。
有可能小伙伴你会对病毒/蠕虫没有概念,那我们来举一个简单的例子吧。平时玩电脑的时候,你正在听歌,突然间收到未知的“朋友”发给你的一个m3u音频文件,并引导你进行电击——“这首歌很好听呢,你也听听”。
凑巧的是,你电脑刚好了就装了VUPlayer.exe 这个播放器,不多想,就打开这个音乐想听听看,奇怪的是,软件闪了一下就退出了,你以为是文件损坏了了吧。其实,这个时候,你已经被黑了,没错!这就是那个播放器。
很普通的播放器,但是它存在致命缓冲区溢出漏洞。我们构造10000个A的m3u文件,然后用调试器附加VUPlayer.exe,打开文件:
我们看到下面的内存全部被字符A覆盖了,其实就是使用了不安全的lstrcpyA函数导致溢出,这个是栈溢出,还有一种是堆溢出,但本质是一样的。
证明有溢出漏洞之后,往往需要进一步分析,不同的人目的不竟相同,但需要注意的是,也有很多“黑市”的不法分子会攻击政府机构或企业的系统,盗取机密文件等,这是非常危险的。
缓冲区溢出是代码中固有的漏洞,除了在开发阶段要注意编写正确的代码之外,对于用户而言,一般的防范措施为
1、关闭端口或服务,管理员应该知道自己的系统上安装了什么,并且哪些服务正在运行。
2、安装软件厂商的补丁,需要及时安装漏洞补丁。
3、在防火墙上过滤特殊的流量,无法阻止内部人员的溢出攻击。
4、检查关键的服务程序,看看是否有可怕的漏洞。
5、以所需要的最小权限运行软件。
6、可借助安防软件提升操作系统安全级别,比如优炫操作系统安全增强系统。
全部0条评论
快来发表一下你的评论吧 !