缓冲溢出
;
程序跳转
;
长跳转缓冲区
;
近些年来
,
黑客攻击事件频繁发生
,
尤其是缓冲区溢出漏洞攻击占据了网络远程攻击的绝大多数
.
因为这类攻击可以使任何人获得系统主机的完全控制权
,
所以它代表了一类十分严重的攻击
.
缓冲区溢出攻击之所以常见
,
是因为它太常见了
,
且易于实现
,
这完全是软件发展史上不可避免的问题
.
缓冲区漏洞是程序员在编写程序时未检查内存空间
,
导致内存泄漏而引起
,
以下我们先来简单了解一下它
:
一、
认识缓冲区溢出
缓冲溢出是一种系统攻击的手段
,
借着在程序缓冲区编写超出其长度的代码
,
造成溢出
,
从而破坏其堆栈
,
使程序执行攻击者在程序地址空间中早已安排好的代码
,
以达到其目的
.
一般黑客攻击
root
程序
,
然后执行类似
exec(sh)
的代码获得
root
的
shell.
它造成了两种严重的后果
:
1.
覆盖堆栈的相邻单元
.
使程序执行失败
,
严重可导致系统崩溃
.
2.
可执行认识指令代码
,
最后获得系统
root
特级权限
.
现在很多人使用
C
或
C++
编写程序
,
但同时太多的人忽略了对其的数组边界检查和类型安全检查
,
所以现今的大多数溢出都和
C
语言有关
, C
语言中中有可能产生溢出的函数有
:char s[n],strlen(s),strcpy(dst, src),p=malloc(n),strcat(s,suffix)
等等
,
所以我们要尽可能地避免使用这些危险函数
,
即使使用
,
也一定要做严格的检查
.
为容易理解
,
我们来看一个简单的程序
:
/*
* example.c
* written by Devil_Angel <Devil___Angel@126.com>
* gcc –o example example.c
*/
void func(char * str)
{
char buf[8];
strcpy(buf, str);
printf(“%sn”,buf);
}
int main(int argc, char * argv[])
{
If(argc >1)
Func(argv[1]);
}//end of main
该程序在输入时
,
并没有对
str
的大小进行检查便直接送入数组
buf,
一旦输入超出
buf
长度
,
就产生了最简单的溢出
,
当然象这样的溢出一般只会出现
Segmentation fault
错误
,
而不能达到攻击的目的
.
这里并没有进一步深入分析
,
只是让大家对溢出有一个大概的概念
,
在以后将会对其做进一步的分析
.
二、
缓冲区溢出漏洞攻击方式
最常见的攻击手段是通过制造缓冲区溢出使程序运行一个用户
shell,
在通过
shell
执行其他命令
.
若该程序输入
root
且有
suid
权限的话
,
攻击者就获得了一个有
root
权限的
shell,
此时就可以对系统进行随意操作了
.
下面我来介绍一下如何控制程序跳转到攻击代码
:
l
打开记录
(Activation Records)
在程序中
,
每一个函数调用发生
,
在堆栈中会留下一个
Activation Records,
它包括函数结束时返回的地址
,
攻击者通过溢出这些自动变量
,
使地址指向攻击程序代码
.
通过改变程序的返回地址
,
当调用结束时
,
程序就跳到攻击者设定的地址
,
而不是原地址
.
这类溢出被称为
stacks mashing attack.
l
函数指针
(Function Pointers)
void(*foo)(1)
定义一个返回函数指针的变量
foo, Function Pointers
可用来定位任何地址空间
.
所以只需在任何空间内的
Function Pointers
附近找到一个能溢出的缓冲区
,
然后溢出它来改变
Function Pointers.
在某时刻
,
当程序通过
Function Pointers
调用函数时
,
程序的流程就按黑客的意图实现了
(
典型的溢出程序有
:Linux
下的
Superprobe
程序
).
l
长跳转缓冲区
(Longjmpbuffers)
在
C
语言中
,
包含了一个简单的检验
/
恢复系统
,
称为
setjmp/longjmp.
即在检验点设定
setjmp(buffer),
用
longjmp(buffer)
恢复
.
但若攻击者能够进入缓冲区空间
,
则
longjmp(buffer)
实际上跳转到攻击者的程序代码
.
像
Function Pointers, longjmp
缓冲区能指向任何地方
,
所以攻击者要做的就是找到一个可供溢出的
buffer
即可
.
最常见的是在一个字符串中综合了代码植入和打开记录
.
攻击者定位或提供溢出的自动变量
,
然后向程序传一个超大字符串
,
在引发
buffer
溢出改变打开记录时植入程序代码
,
由此达到入侵系统的目的
.
三、
应对远程缓冲区溢出攻击
对付缓冲区溢出攻击的方法不少
,
但常见的也是最重要有一下四种方式
:
1.
编写严格的代码
编写正确严格的代码是一件有意义但非常耗时的工作
,
有
C
程序设计或汇编语言经验的人会深有体会
,
尽管软件的发展经历了不短的时间了
,
但漏洞程序依旧存在
,
因此人们开发了一些工具和技术来帮助经验不足的程序员编写安全的程序
.
例如高级查错工具
,
如
faultinjection
等
.
这些工具的目的在于通过人为随机地产生一些缓冲区溢出来寻找代码的安全漏洞
.
但由于
C
语言的特点
,
这些工具不可能找出所有的缓冲区溢出漏洞
.
所以
,
侦错技术只能用来减少缓冲区溢出漏洞
,
并不能完全地消除其存在
.
错误的消除还是要靠程序员来编写
.
2.
不可执行堆栈数据段
通过操作系统时数据断地址空间不可执行
,
从而使得攻击者不能执行被植入的攻击代码
,
但攻击者不一定是非要植入攻击代码来实现缓冲区溢出的攻击
,
所以这种方法还是存在很多弱点的
.
3.
利用程序编译器的边界检查
植入代码是引起缓冲区溢出的一个方面
,
改变程序执行流程是另一方面
.
而利用编译器边界检查则使得缓冲区溢出不可能实现
,
从而完全消除了缓冲区溢出的威胁
,
但相对而言代价较大
.
4.
指针完整性检查
程序指针完整性检查和边界检查略微不同
. 程序指针完整性检查在程序指针被改变之前检测.因此,即便攻击者成功改变了程序的指针,也会因先前检测到指针的改变而失效,这样虽然不能解决所有问题,但它的确阻止了大多数的缓冲区攻击,而且这种方法在性能上有很大的优势,兼容性也很好.
从长远来看
,要想从根本上消除缓冲区溢出攻击,需要对编程模式或CPU体系的基础性修改才能解决问题. 不过,随着信息技术的飞速发展和人们对网络安全的重视程度不断加深, 缓冲区溢出攻击总会有解决的一天.