您的位置:首页 > 其它

空间格局随机化ASLR

2010-01-27 20:39 183 查看
1.概述
在前面安全编码实践中我们介绍过GS编译选项和缓存溢出,以及数据保护DEP。首先,缓存溢出的直接后果就是可能导致恶意代码的远程执行,于是编译器提供了GS保护。但是,GS选项有自身的局限,存在若干方法可以绕过GS选项的保护。于是进一步,操作系统提供了数据执行保护,即DEP,以及与之对应的NXCOMPAT编译选项。
那么是不是现在我们就可以高枕无忧了?在安全领域中,系统的攻防是一个不断发展进化的过程。DEP提出后,就出现了针对DEP的Ret2libc攻击手段。这一点我们曾在介绍DEP的安全编码实践文章的最后简单提及过。
ASLR(AddressSpaceLayoutRandomization),地址空间格局的随机化,就是用来防范Ret2libc攻击手段的另一个重要的安全特性。那么,什么是Ret2libc攻击,ASLR的原理是什么,开发人员如何使用这个安全特性,就是我们这篇文章要探讨的内容。
2.DEP和Ret2libc攻击
2.1DEP对堆栈溢出的保护
在栈溢出介绍中提及到Windows体系结构下函数堆栈布局(地址从高向低)如下:

调用参数
返回地址
EBP上层函数堆栈基址
异常处理代码入口地址
(如果函数设置异常处理)
局部变量
表1:Windows系统的函数堆栈结构

如果发生堆栈溢出,恶意代码通过覆盖在堆栈(stack)上的局部变量,从而修改函数的返回地址,而导致恶意代码执行。下面是这类攻击方式的堆栈结构的一个典型例子。

调用参数
覆盖方向—>
恶意代码
返回地址
恶意代码的入口地址
EBP上层函数堆栈基址
溢出的变量覆盖区域,往往包括必要的填充字节
异常处理代码入口地址
(如果函数设置异常处理)
局部变量
表2:堆栈溢出时的堆栈结构
当DEP保护机制被使用后,由于恶意代码是存放在系统的数据页面(堆栈页面上),那么函数返回时,指令寄存器EIP将跳转到恶意代码的入口地址。此时该页面是非可执行的(non-executable),于是DEP就会触发系统异常而导致程序中止。
2.2Ret2libc攻击

在上述的DEP保护机制中,可以看到关键是在函数返回时EIP跳转到了非可执行页面时被DEP检测到。那么Ret2libc的攻击原理是,攻击者设定的函数的返回地址并不直接指向恶意代码,而是指向一个已存在的系统函数的入口地址。由于系统函数所在的页面权限是可执行的,这样就不会触发DEP异常。

那么,攻击者应该将EIP控制指向那个特殊的系统入口函数?一个例子是在Unix系统下,libc是一个共享的C动态执行库,里面有许多非常有用的函数,例如system函数。它的定义如下:
intsystem(constchar*string);

函数system()可通过运行环境来执行其它程序,例如启动Shell等等。那么,攻击者就可以通过构造以下的堆栈结构【1】:

调用参数
覆盖方向—>
/bin/sh
虚假的返回地址
返回地址
system函数的入口地址
EBP上层函数堆栈基址
溢出的变量覆盖区域,往往包括必要的填充字节
异常处理代码入口地址
(如果函数设置异常处理)
局部变量
表3:Ret2libc攻击的堆栈结构

这样,当发生堆栈溢出的函数返回时,EIP跳转到system函数。因为system函数本身就是可执行的,这时不会产生DEP异常。攻击者通过构造system函数的调用参数来可以启动其它程序。在攻击过程中,函数返回到libc库(returntolibc)是关键,这也就是Ret2libc名字的来由。
细心的读者也许已经发现,在表3中,没有任何恶意代码被插入。攻击者虽然可以通过system或者其它系统函数来执行很多敏感的操作,但在多数情况下,还是更希望可以执行自身定制的恶意代码。如何可以做到这一点?于是在最初的Ret2libc的攻击方式的基础上,又发展出特别针对Windows系统攻击的手段。它的原理是通过VirtualProtect函数来修改恶意代码所在内存页面的执行权限,然后再将控制转移到恶意代码。
VirtualProtect是Windows系统kernel32.dll提供的函数,其功能是修改调用进程所在虚拟地址空间(virtualaddress)的内存区域的保护权限。它的定义如下:

BOOLWINAPIVirtualProtect(
__inLPVOIDlpAddress,
__inSIZE_TdwSize,
__inDWORDflNewProtect,
__outPDWORDlpflOldProtect
);

攻击者构造以下的堆栈结构【2】来调用VirtualProtect:

调用参数
覆盖方向—>
恶意代码
lpflOldProtect
设定可执行权限参数
恶意代码页面的大小
恶意代码所在内存页面的基址
恶意代码的入口地址
返回地址
VirtualProtect函数的入口地址
EBP上层函数堆栈基址
溢出的变量覆盖区域,往往包括必要的填充字节
异常处理代码入口地址
(如果函数设置异常处理)
局部变量
表4:使用VirtualProtect攻击的堆栈结构

首先,当发生堆栈溢出的函数返回时,EIP跳转到VirtualProtect函数。注意到这里攻击者特别构造将恶意代码的入口地址作为VirtualProtect函数退出时的返回地址。由于在VirtualProtect的执行过程中,恶意代码所在的页面被修改为可执行权限,这样当VirtualProtect返回时,EIP再跳转到恶意代码时就不会触发任何DEP异常。
除了使用VirtualProtect函数,攻击者还可以使用其它函数,例如NtSetInformationProcess等等。
3.ASLR和/dynamicbase链接选项
在上面对Ret2libc攻击方式的介绍中,我们看到最为关键的一点是攻击者事先预知了特定函数,如system或VirtualProtect的入口地址。在WindowsXP或Windows2000上,这些函数的入口地址是固定的,即攻击者事先可以确定的。

在WindowsVista中引入了ASLR安全特性。它的原理就是在当一个应用程序或动态链接库,如kernel32.dll,被加载时,如果其选择了被ASLR保护,那么系统就会将其加载的基址随机设定。这样,攻击者就无法事先预知动态库,如kernel32.dll的基址,也就无法事先确定特定函数,如VirtualProtect,的入口地址了。
ASLR是系统一级的特性。系统动态库,如kernel32.dll,加载地址,是在系统每次启动的时候被随机设定的。

下面是一个简化的ASLR演示程序【3】。

//aslr.cpp:DemothedynamicbaseofDLLsduetoASLR
//

#include"stdafx.h"
#include<windows.h>
#include<stdio.h>

voidfoo(void)
{
printf("Addressoffunctionfoo=%p/n",foo);
}

int_tmain(intargc,_TCHAR*argv[])
{
HMODULEhMod=LoadLibrary(L"Kernel32.dll");
//Note—thisisforreleasebuilds
HMODULEhModMsVc=LoadLibrary(L"MSVCR90.dll");

void*pvAddress=GetProcAddress(hMod,"LoadLibraryW");
printf("Kernel32loadedat%p/n",hMod);
printf("AddressofLoadLibrary=%p/n",pvAddress);
pvAddress=GetProcAddress(hModMsVc,"system");
printf("MSVCR90.dllloadedat%p/n",hModMsVc);
printf("Addressofsystemfunction=%p/n",pvAddress);

foo();

if(hMod)FreeLibrary(hMod);
if(hModMsVc)FreeLibrary(hModMsVc);
return0;
}

这段程序的目的是输出kerner32.dll和msvcr90.dll的基址,loadlibrary和system函数的入口地址,以及应用程序本身一个函数foo()的入口地址。
使用ASLR非常简单。从VisualStudio2005SP1开始,增加了/dynamicbase链接选项。/dynamicbase选项可以通过ProjectProperty->ConfigurationProperties->Linker->Advanced->RandomizedBaseAddress,或直接修改linker的命令行编译选项即可。
在VisualStudio2008环境,用Win32ConsoleApplication类型,编译链接演示程序。注意,如果使用VisualStudio2005SP1的话,需要将msvcr90.dll更改为msvcr80.dll。
如果程序没有使用ASLR功能的话,在WindowsVista下运行。输出的结果是:
Kernel32loadedat763F0000
AddressofLoadLibrary=7641361F
MSVCR90.dllloadedat671F0000
Addressofsystemfunction=6721C88B
Addressoffunctionfoo=00401800

重启系统

Kernel32loadedat76320000
AddressofLoadLibrary=7634361F
MSVCR90.dllloadedat6A340000
Addressofsystemfunction=6A36C88B
Addressoffunctionfoo=00401800

我们看到,即使程序本身没有使用ASLR,Kernel32.dll和MSVCR90.dll的加载地址也发生了变化。这是因为这两个库都已经选择了被ASLR保护。但是应用程序自身foo()函数的地址是固定的。
如果程序使用ASLR功能的话,在WindowsVista下运行。输出的结果是:
Kernel32loadedat763F0000
AddressofLoadLibrary=7641361F
MSVCR90.dllloadedat671F0000
Addressofsystemfunction=6721C88B
Addressoffunctionfoo=003B1800

重启系统

Kernel32loadedat76320000
AddressofLoadLibrary=7634361F
MSVCR90.dllloadedat697A0000
Addressofsystemfunction=697CC88B
Addressoffunctionfoo=00871800

应用程序自身函数foo()的加载地址也随着系统重启发生了变化。即一旦使用了/dynamicbase选项,生成的程序在运行时候就会受到ASLR机制的保护。
4.ASLR的局限
首先,ASLR安全特性只在WindowsVista和其后的Windows版本(如WindowsServer2008)中实现。

其次,
ASLR是需要和DEP配合使用的。如果CPU不提供对于DEP的硬件支持,或者应用程序没有选择被DEP保护的话,恶意代码一旦可以执行,就可以通过程序进程表结构来获得特定DLL的加载基址。

就性能和
兼容性
而言,
ASLR的实现上都做了考虑,没有太多的影响。一个范例是MicrosoftOffice2007。Office2007的程序全面使用ASLR功能,并没有发现对其
性能和
兼容性带来太大的影响【4】


5.总结
ASLR安全特性在WindowsVista和其后的Windows版本(如WindowsServer2008)中实现。它可以防范基于Ret2libc方式的针对DEP的攻击。ASLR和DEP配合使用,能有效阻止攻击者在堆栈上运行恶意代码。建议开发人员使用/dynamicbase链接选项让开发的应用程序或动态链接库使用ASLR功能。

6.参考文献
【1】Bypassingnon-executable-stackduringexploitationusingreturn-to-libc,http://www.infosecwriters.com/text_resources/pdf/return-to-libc.pdf,c0ntex
【2】ABriefHistoryofExploitationTechniques&MitigationsonWindows,http://hick.org/~mmiller/presentations/misc/exploitation_techniques_and_mitigations_on_windows.pdf,MattMiller
【3】WritingSecureCodeforWindowsVista,MichaelHoward,DavidLeBlanc
【4】UseofASLR,NX,etc,http://blogs.msdn.com/david_leblanc/archive/2008/03/14/use-of-aslr-nx-etc.aspx,DavidLeBlanc

from:http://blog.csdn.net/chengyun_chu/archive/2009/10/09/4644227.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: