在CU上看到的一篇关于ExecShield的介绍转载过来与大家分享下
Redhat ExecShield whitepaper翻译
前一段时间事情一直比较多,昨天抽空读了一下Redhat关于exex-shield的whitepaper,个人感觉收获不小,解决了不少疑问,所以花了一些时间将其翻译了出来,和大家分享,翻译中如果有错漏指出请大家帮忙指正。
//------------------------------------------------------------------------------------
New Security Enhancements
in Red Hat Enterprise Linux
v.3, update 3
Introduction
(略:主要就是说现在的网络和系统都多麽不安全,记得看过一本关于安全的书,一开始作者就将“我认为世界上没有安全的计算机,即使是用混凝土将其封闭起来”,我半天没想明白什麽意思,后来明白了作者其实是想说他水平极高,而且还会拆混凝土 :) ,开个玩笑言归正传。)
Buffer Overflows
(略:此部分介绍了缓冲区溢出的原理,我想不太了解的朋友也不太会看这帖)
Countering Buffer Overflows(缓冲区溢出对策)
原文翻译:
如前所述,大多数exploit程序都是通过对堆栈返回地址进行操作,并将其设为想要的值来实现 。
返回地址是子程序调用的基本特性,并且不可能被完全替代。所以从逻辑上,首先
要做的就是保证程序的返回地址指向可信任的代码而不是恶意的攻击代码。这也正是Execshield,
Intel 和AMD提供的NX(Not eXcutable)技术和微软的Data Execution Prevention技术所采取的方式。
偶然关注这个话题的人也许会想,这些技术为何产生,这种行为(not excutable)为什麽不是
本来就有的呢?原因要追溯到x86架构的底层。在x86架构中,CPU对有权限从一段内存中读取或是执行一段代码是不加区分的。出于对8086的兼容性,这种机制被保留下来时,但这显然是我们所不愿接受的。
The Segment Limit Approach(段限制策略)
ExecShield 以及与其类似的技术(PaX) 会估算一个地址,并由此将执行代码和读写
代码分开(一个源自与80386特性相类似的机制 (Ring???)).增加了段边界限定的
效果就是前N兆字节的虚拟地址是可执行的,而剩下的则不可。N的值由操作系统内核
来计算。
因为段边界地址的存在,操作系统必须能够保证执行代码处于段边界的前部。 而数据段
尤其堆栈段(在ELF中对应.date .bss 和.stack段)必须位于高位内存。
当越界执行发生时(violation of the execution permission)程序会触发段错误
(segmentation fault (signal 11???))并且终止程序。这种行为等价于一个应用
程序试图违反读取或写入权限时所触发的错误。
以这种一分为二的方式,段限制基本实现了读写代码和执行代码的分离。
对于几乎所有的程序,内核将执行代码置于低位内存,而将数据与这部分分离。
在某些少见的特殊情况下(如XFree86),有可能因为估算的边界地址不太准确
,而造成的保护的不完全。(看来X确实不太安全,还是少使为妙)。
Intel and AMD NX Technology(Intel和AMD的NX技术)
AMD和Intel都认识到了x86架构中缺乏分离执行代码和读写代码的不足。
在AMD系列中,AMD以一种向下兼容的方式扩展了架构,在以有的内存权限中添加了
NX权限。和已有的内存读写权限相配合,内核以4kb为粒度(虚拟页大小,应该可以更改)
对程序的NX权限进行控制。Intel和其他X86制造商(还有谁呀?)宣布他们将在未来的产品
中对NX技术进行支持。
NX技术相比上面提到的段估算限制技术(segment limits approximation)更胜一筹。
因此伴随着硬件支持,RHEL3中的ExecShield将采用NX技术取代段限制技术。
而且NX技术相比段限制技术对于内核模块同样适用。
关于NX技术还有一点要提的是,因为需要保存和处理额外的权限信息,其只能在PAE64位
页表格式下工作( PAE技术使32位x86处理器能够管理多余4GB的内存)。
而PAE页表并不能在RHEL3支持的5中x86处理器中,全部正常工作。NX技术还导致约
6%的性能下降。因为这些限制,PAE和NX技术只在kernel-smp和kernel-hugemem中得
到了支持,而单处理器内核则不支持。
Randomization(随机化)
如果想要进行成功的缓冲区溢出攻击,就必须知道堆栈缓存的近似地址,从而正确的
用exploit code地址覆盖函数返回地址。这可能听起来很难,但通过练习可以很简单
的达到。每个运行RHEL的系统都有相似的应用程序,二进制文件和库。因为这些相似性
,许多运行as3的系统都有着相似甚至相同的地址。一个写exploit程序的人可以简单的
通过在自己的机器上实验得到这些和其他系统相似的地址(+ NOP slide)。这并不只是RHEL
的问题,所有广泛使用的的系统都会遇到这种"same environment"问题。
另一种写exploit程序的方法(call the glibc)也离不开返回地址,所以这种方式并不
需要代码执行。只须知道要调用的函数的准确地址就行(system.map???).
通过以上观察,我们发现两种方式都是以对精确内存地址的了解为基础的。
RHEL3-U3中的ExecShield技术因此对一些关键的组件赋予了随机的地址。
这些随机化的偏移地址,通过使其几乎不可能确定准确的地址,而提高了系统的安全性。
现在这些地址因机器而异,并切对于同一台机器没次启动程序地址也不同。
在RHEL3 update3中,ExecShield对一个程序的以下组件自动进行随机化。
1 堆栈本身
2 shared libraries地址
3 程序heap段起始
Remaining Randomization: PIE Binaries
到现在,我们已经讨论了应用程序代码本身在内存中地址的随机化。但在Linux中
应用程序通常是以一种预选定地址的形式被编译成二进制码的(即ELF中的可执行
文件格式)。如果这样,内核便无法将其地址随机化。而缺乏随机化正是缓冲区
溢出保护的弱点。
因此Red Hat开发并贡献给了GNU Compiler Collection toolchain一项称为PIE即position
Independent Executable的技术。PIE二进制文件以一种特殊的形式被编译,使它们可以在
整个程序地址空间中随机定位。有ExecShield技术的内核,这些二进制文件被加载的地址
是随机的,而在没有ExecShield和PIE支持的内核中这些二进制文件加载的地址是可预测的。
并不是所有的程序都适合被编译为PIE格式。因为PIE二进制文件必须可以被重定位,
所以他们必须被编译为Position Independent Code (PIC)(注:废话,作者没有言明,应该
就是ELF三种格式中的可重定位文件格式吧)。PIC代码就像共享库一样(共享库也是被编译为
PIC格式,即ELF三种格式中的共享object文件)对于运行时效率有一些影响。
想要将文件编译为PIE格式,需要对编译器和连接器分别添加-fpie 和-pie命令行参数。
因为PIE是一项新的技术,所以并没有在RHEL3中对所有软件用该技术编译。Red Hat将在
以后的升级中增加PIE技术的使用。在RHEL3 update3中,几个使用率很高的程序被编译为
PIE格式,包括:
1 The Bind DNS nameserver
2 The Samba SMB/CIFS fileserver
3 The Squid HTTP proxy
4 The vsftpd FTP server
除此以外,几个小一些的程序也被编译为PIE(rusers and cron, the NFS RPC daemons)。
想要查看一个程序是否被编译为PIE,使用以下命令:
#readelf -h -d /usr/sbin/smbd | grep 'Type:.*DYN'
#Type: DYN (Shared object file)
Compatibility(兼容性)
在对RHEL3的新安全特性进行设计时,对已有应用程序的兼容性被优先考虑,以使新的改变
不会对已有应用程序造成影响。使堆栈不可执行的主要兼容性问题是对那些需要依赖堆栈
段或堆可执行来运行的程序。很多种原因造成了这种行为,包括incorrect
assumptions in the application(???) 的情况(例如JVM的缓存,其它动态语言
也可能有这种问题), 或是某些特定的programming constructs(???)或是编程语言
出于某些实际的原因需要堆栈段可执行。
对这个兼容性问题的解决方案,可以通过在二进制文件或库中添加特定的flag来实现。
这个flag值表示了该程序或库是否依赖堆栈段可执行机制来运行。
Red Hat通过向GCC贡献的代码,使程序在编译时自动根据使用的programming constructs
或是程序设计语言添加该flag。
而且,没有该flag标志的二进制程序或是库,内核会假定其需要堆栈可执行机制来运行
从而保证最大向下兼容性。
Table 1. Overview of ELF flag behavior
-----------------------------------------------
Flag value Resulting behavior
-----------------------------------------------
Flag present, RWE Stack is executable
Flag present, RW Stack is not executable
Flag absent Stack is executable
-----------------------------------------------
想要查看一个二进制文件或库是否有该标志值,输入以下命令:
#eu-readelf -l /bin/true | grep GNU_STACK
GNU_STACK 0x000000 0x00000000 0x00000000 0x000000 0x000000 RW
#eu-readelf -l /usr/bin/eu-nm | grep GNU_STACK
GNU_STACK 0x000000 0x00000000 0x00000000 0x000000 0x000000 RWE
0x4
系统管理员,想要查看一个进程是否有堆栈段执行保护,可以访问
http://people.redhat.com/arjanv/lse...略�lsexec脚本。
How Well Does It Work(效果如何)
在2003年11月1日至2004年8月11日,在16个安全等级高于DoS的漏洞中,四个
内核安全性漏洞ExecShield没能提供保护,但成功的阻止了11个缓冲区溢出漏洞
,并且还成功的阻止了一个堆溢出漏洞。因此ExecShield得到了75%的成功率。
值得注意的是这些exploits程序在没有NX技术支持的处理器上依然得到了阻止
(因为Randomization和PIE)。