APP下载

基于地址随机和段隔离的全局偏移表保护方法

2016-07-19林键郭玉东周少皇

计算机应用 2016年7期
关键词:寄存器全局漏洞

林键 郭玉东 周少皇

摘要:在可执行和可链接格式(ELF)的可执行程序中,存在一个全局偏移表(GOT),用于存放引用库函数的绝对地址,但是在Linux系统中,GOT解引用和GOT覆写是两种比较常用的漏洞利用方法。通过分析GOT的特性,提出并实现了基于地址随机和段隔离的GOT保护方法。通过修改Linux的可执行程序加载器,将与GOT有数据指向关系的节均加载到随机内存地址;同时使用段隔离技术,对GOT的代码引用的指令使用一个新的段寄存器进行间接引用。实验结果证明,该方法不仅能够有效地防御针对GOT的漏洞利用方法,而且性能损耗极低,只有平均2.9ms的额外开销。

关键词:

全局偏移表保护;地址随机;段隔离;全局偏移表解引用;全局偏移表覆写

中图分类号: TP309.2 文献标志码:A

0引言

Linux是一套免费使用和自由传播的操作系统,广泛应用于文件服务器、数据库服务器、Web服务器、应用程序服务器等服务器之中。服务器作为提供基本服务的设备,需要保证其提供服务的可靠性和安全性。形形色色的针对服务器上的攻击,已经导致诸多严重的后果:敏感数据丢失或损失、系统受损以及经济损失等。其中,通过挖掘软件漏洞,利用漏洞进行攻击是最为常见的攻击方法。这些漏洞的成因主要是在软件设计和实现方面对如何保护系统考虑不周,在开发实践方面对消除会导致漏洞的实现瑕疵关注不够。攻击者在发现软件产品中的漏洞后,可以迅速开发出漏洞利用的脚本,继而使用这些脚本威胁计算机的安全。为了增加漏洞利用的难度,防御者在系统中部署了漏洞利用缓解技术,如地址空间布局随机化(Address Space Layout Randomization, ASLR)[1]和数据执行保护(Data Execution Prevention, DEP)[2]等,但是即使在部署了以上缓解技术的系统中,通过利用全局偏移表(Global Offset Table, GOT),可以绕过这些缓解技术从而实现漏洞的完美利用。

全局偏移表,是Linux下可执行程序中一个专门的节,用于存放依赖库函数的目的地址。因为延迟解析机制,全局偏移表中的库函数只有在第一次真正引用时才解析,并在解析后将目的地址写入全局偏移表,为此,全局偏移表具有可写权限。目前Linux虽然采用地址空间布局随机化技术,对栈、堆、共享库进行了随机化,但是可执行程序仍然加载到固定内存地址,因此全局偏移表仍然加载到固定内存地址,因此,全局偏移表是固定内存的可写函数指针集合。正是由于全局偏移表的这种特性,存在两种常见的使用全局偏移表的漏洞利用方法:全局偏移表解引用技术[3-4]和全局偏移表覆写技术[5-6]。全局偏移表解引用技术,通过信息泄露漏洞,读取全局偏移表中已解析的库函数地址,可以得到该函数的真实地址,为此可以计算得到相应动态链接库的基地址,从而绕过地址空间布局随机化的防护;全局偏移表覆写技术,通过任意地址写漏洞,修改全局偏移表中库函数的地址,能够劫持程序控制流。

针对这些攻击方式,有研究者提出了一些相应的保护方法。完全地只读重定位(Full Relocation ReadOnly, Full RELRO)[7-8],禁用延迟解析,在链接器初始化阶段将所有全局偏移表中函数的值解析为真正函数的地址,然后将全局偏移表所在内存页标记为只读权限,很好地防止了对全局偏移表的改写。安全全局偏移表(Secure Global Offset Table, SecGOT)[9-10],通过重写可执行和可链接格式(Executable and Linkable Format, ELF)[11]文件,并用定制的链接器进行链接,在链接时将全局偏移表移动到随机的内存中;同时修改访问全局偏移表的指令。

Full RELRO方法中,全局偏移表仍然位于固定内存地址,因此可以对全局偏移表内容进行直接读取,不能防止全局偏移表解引用。在启动时需对可执行程序使用的所有库函数进行解析,而函数符号解析过程相对比较耗时,对程序启动过程的性能影响较大。SecGOT方法随机不够彻底,虽然攻击者不能立刻知道全局偏移表地址,但是指向全局偏移表的数据和代码引用全局偏移表的指令仍位于固定地址,通过读取这些固定地址,可以计算得到全局偏移表的地址。针对这两种方法的不足,本文提出一种新的保护方法:基于地址随机和段隔离的全局偏移表保护(Global Offset Table protection based Randomization, GOTRand)方法。在可执行程序加载时,不仅将全局偏移表加载到随机的内存中,而且将所有对全局偏移表有数据指向关系的节也加载到随机内存中。同时,引用一个新的段寄存器,利用段的隔离特性,将所有对全局偏移表进行代码引用的指令修改为使用该新段寄存器进行间接引用。

1ELF结构

ELF又称可执行和可链接格式,是Linux系统的主要可执行文件格式。ELF有详细的格式规范[8],此处仅介绍ELF中与GOT有关联的结构。在ELF中,存在一个动态节,用于提供动态链接所需的信息,其中包含了GOT节所在地址。GOT节,用于存在全局函数的目的地址,也就是本文所要重点保护的GOT。

为了实现对GOT中外部函数的调用,在ELF中还定义了一个过程链接表(Procedure Linkage Table, PLT)。GOT中的每一个函数在PLT中都有一个对应的项,记GOT第n个函数在PLT中的对应项为PLTn,如第1个函数在PLT中的对应项为PLT1。特殊地,在PLT节的最开始,定义了一个特殊的表项,记为PLT0,不对应任何函数。PLT对应的结构如图1所示。除了PLT0之外,其余各项都是由三条指令构成的程序片段。PLT项中第一条跳转指令(jmp)完成对GOT的代码引用。

ELF中,对符号的动态解析采用了一种延迟加载的方式[9]。如图2所示,动态链接器初始化后,GOT节中除了前3项用于特殊用途外,其余的各项均指向到相应的PLT项的第2条指令。当首次调用某函数时,程序才通过符号解析函数_dl_runtime_resolve()解析获取函数的真正地址,并填入到GOT对应项中。

2GOTRand的设计

针对GOT的漏洞利用技术中,GOT解引用技术可通过一次任意地址信息泄露将位于固定地址的GOT中的内容泄露出来,即得到函数的真正地址,根据这个函数在库中的相对偏移,可以得到整个动态库加载的基地址。GOT覆写技术,则是通过一次任意地址写漏洞,将位于固定地址的GOT中的内容改写为恶意代码的入口点,从而劫持程序的控制流程。

通过分析两种漏洞利用技术,可以发现它们主要利用了GOT的两个特点:1)GOT中存放的都是函数指针,且具有可写权限,通过修改GOT即可获取到程序执行流程;2)GOT加载到内存中的地址是固定的,通过对ELF程序的解析,可以获取其GOT加载的内存地址。第1个特点是由GOT和延迟解析的特性所决定的,对其改进将带来较大性能损耗。故主要考虑从第2个特点进行改进,对GOT地址进行随机化,使其地址不再固定。随机化处理需要保证以下两点:1)GOT地址改变之后程序依旧能正常运行;2)GOT随机之后能够有效防御针对GOT的攻击方法:GOT解引用和GOT覆写,保证在随机后GOT的地址不能被攻击者间接获取得到。

2.1程序的正常运行

在ELF中,不同的节之间有一定的指向关系,通过某个节可以知道另一个节的地址。在对某个节内存地址作改变之后,只要能保证节之间正确的指向关系,程序依旧能正常运行。

在动态链接器初始化阶段,链接器解析ELF文件的程序头表,获取程序的动态节所在地址,保存到自己的链接映射结构link_map中。然后解析动态节,得到GOT节所在地址,改写GOT节的第1项为link_map的开始地址,第2项为符号解析函数_dl_runtime_resolve()的入口地址。在程序运行阶段,当程序需要调用某库函数,通过对应的PLT完成对GOT的代码引用。ELF中各节的指向关系及使用时段如图3所示。

2.2防御方法的有效性

GOT地址随机化之后,需要保证新的GOT地址不能被攻击者轻易获取到。除了保证新的GOT地址是随机的之外,还需要防止攻击者能够通过读取固定地址内存中的数据计算出GOT所在的地址。经分析,主要存在以下两种获取途径:读取PLT中代码或者通过读取指向GOT的节。

读取PLT的代码,即通过读取PLT所在地址的数据,通过反汇编,获取GOT的地址。假设第n个外部函数fn在GOT中的地址0x0804a004,那么PLTn中第1条指令jmp *0x0804a004对应的机器码为FF 25 04 A0 04 08,因此攻击者只需要读取PLTn的前6个字节,通过反汇编就可以获取fn在GOT中的地址。如果想让攻击者无法通过反汇编代码得到引用的地址,引用Intel处理器的段寄存器是最好的选择。

Intel处理器中一共有6个段寄存器:CS、DS、ES、SS、FS、GS,其中:CS为代码段的基地址,DS和ES为数据段的基地址,SS为堆栈段的基地址,FS和GS用于特殊目的的基地址。假设引用的段寄存器为FS,对应的段的基地址设置为0x08040000,那么PLTn中第1条指令将为jmp *fs:0xa004,对应的机器码为64 FF 25 04 A0 00 00,在不知道FS寄存器基地址的情况下,将不会知道GOT所在地址。而FS寄存器的基地址是在内核中进行设置,在用户态没有直接方法可以得到。

读取指向GOT的节是通过ELF的程序头表、动态节和GOT节的数据指向关系,只要其中任意一个在内存中地址是固定的,就可以通过信息泄露,依次向下解析,直到获取到GOT节的地址。因为仅仅在动态链接器初始化阶段才会根据程序头表得到动态节地址。故在动态链接器初始化完毕之后,修改程序头表中的对应项,清除程序头表中的动态段信息。而动态节和GOT节可以同时加载到随机内存地址中。

所以,为了保证GOT地址随机化的有效性,GOT修改后ELF各节的随机情况及指向关系如图4所示,其中灰色部分的节都被加载到随机内存。PLT通过段寄存器实现对GOT的间接引用。在动态链接器初始化后,清除程序头表中的相关项,而动态节和GOT节均加载到随机的内存地址中。这样攻击者就无法通过上述的两种途径计算出GOT所在内存地址。

3GOTRand的实现

本文通过修改Linux内核代码实现GOTRand保护方法的原型系统。GOTRand的总体框架如图5所示,其中在加载时,通过定制加载器将GOT加载到随机内存,并引入段寄存器;在动态链接器初始化完毕之后,清除程序头表中关于动态段的信息。内核对ELF程序的加载函数为load_elf_binary()。通过修改load_elf_binary()函数实现定制加载器,在定制加载器中主要增加ELF节信息的获取、GOT内存地址随机和段寄存器的引入3个模块。

3.1GOT地址的随机

GOT地址随机主要是将GOT节和动态节加载到随机的内存地址。动态节中的数据不需要进行修改,因此只需要给予只读权限。GOT节中数据需要在符号解析完成之后将函数的真正地址写入GOT中,因此需要有可读可写权限。根据两个节的内存属性的不同,申请两页随机的空闲页:只读页和可读可写页。动态节加载到只读页中,GOT节加载到可读可写中。

进程虚拟地址空间预留了多个空洞。其中在32位程序中,程序的加载基地址通常为0x08048000,0x08048000地址之下的内存空间通常都不会被使用到,但是0x08048000以下的内存也不是全部都能够使用的。Linux内核为了防御空指针解引用漏洞,引入了mmap_min_addr机制[12],不允许小于mmap_min_addr值的地址映射内存。mmap_min_addr的值可以由用户配置,在默认的Linux发行版中,通常设置为65536,即低64MB内存不能使用。所以选择在mmap_min_addr到0x08048000地址随机申请两页空间,如图6所示。

3.2段寄存器的引入

通过分析当前Linux系统对段寄存器的使用情况,发现在32位程序中,CS、DS、ES、SS用作基本的段寄存器使用,其基地址均设置为0,GS用于指向线程局部存储(Thread Local Storage, TLS)区域,而FS寄存器一般没有。在64位程序中,CS、DS、ES、SS寄存器的基地址在硬件上被强制为0值,段基地址仅对FS和GS寄存器来说才有意义,FS被用于指向线程局部存储(TLS)区域,而GS寄存器没有被使用。所以,在当前Linux系统的实现中,无论是32位还是64位程序,都刚好有一个空闲未用的段寄存器,刚好可以被用于GOTRand中。在有了空闲段寄存器后,随机地为其设置一个段寄存器基地址,然后对PLT节代码进行补丁,对GOT使用新的段寄存器进行访问。

3.3动态段信息的清除

在动态链接器初始化完毕之后,程序会默认跳转到可执行程序入口点开始执行。这里需要对动态段信息进行清除,因此需要在链接器初始化完毕之后,需要内核接管程序的控制权。通过对动态链接器初始化部分代码的分析,发现程序对GOT的第一次写操作为向GOT的第1项写入自己的链接映射结构link_map的地址。而link_map结构建立好之后,链接器已经不需要通过程序头表中动态段的信息。根据链接器的这个特性,采用写保护的方法来获取程序的控制权。在内核加载ELF程序时将GOT所在的内存页属性标记为不可写。当动态链接器初始化过程中向GOT进行写操作的时候,将会导致处理器产生页故障(Page Fault)异常,将会执行内核的页故障处理函数,从而内核控制了程序的控制权。

按照现有Linux内核的页故障处理流程,对GOT的写入异常将会被判定为用户态的非法访问操作,从而进行非法访问页故障处理。在非法访问页故障处理中通过判断CR2寄存器中保存的异常虚拟地址是否为该进程的GOT的第1项;同时根据异常错误代码的W/R位判断是否为写异常,可以判定动态链接器初始化是否完毕。如果动态链接器初始化完毕,则清除程序头表中的动态段信息;同时将GOT所在内存标记为可读可写。

4测试

在Debian GNU/Linux 6 i686发行版上通过一个已知漏洞的攻击实验,验证了GOTRand的防御效果,并从理论上比较了几种已有保护方法的防御效果。另外,通过使用SPEC CPU2006测试机对GOTRand进行性能测试。结果显示,GOTRand不仅能有效防御GOT解引用和GOT覆写攻击,而且性能损耗很低。

4.1防御效果测试

实验选择了一个公开了漏洞利用程序的漏洞程序进行测试。选择的漏洞程序为Nginx HTTP服务器,对应的漏洞号为CVE20132028[13]。在Nginx HTTP服务器1.3.91.4.0版本的程序中,在ngx_http_parse_chunked()函数中存在整型溢出漏洞。在metasploit的模块中,有对该漏洞进行完美利用的利用程序[14]。

经过测试,metasploit的利用程序能够在默认的debian发行版中成功地获取shell,但是,在GOTRand系统中,不能成功获取shell。

分析该漏洞利用程序,发现该利用程序使用的Return Oriented Programming(ROP)[15]链中使用GOT解引用的方法绕过ASLR,其中假定了localtime_r函数在GOT中的地址默认为0x080b4128,如图7所示,但是在GOTRand中GOT加载到随机地址,所以读取到的地址0x080B4128中的内容并不是函数localtime_r的地址,整个漏洞利用也就失败。

本文还比较了SecGOT和Full RELRO对于GOT解引用的防御效果。SecGOT在动态链接器初始化时,对GOT地址进行了随机;但是攻击者可以先对PLT进行解引用,即可得到GOT所在地址,再对GOT进行解引用,因此只需要构造一个更加复杂一点的ROP[12]链即可成功利用。GOT解引用只涉及到GOT的读操作,因此Full RELRO完全没有防御效果。

对于GOT覆写劫持程序控制流方法,需要知道GOT所在地址并且具有可写权限。在SecGOT中,GOT所在地址可以间接得到,因此不能防御GOT覆写攻击。在Full RELRO中,GOT不可改写,因此不能进行GOT覆写。而在GOTRand中,因为段隔离机制,没有方法能获取GOT所在地址,因此也不能进行GOT覆写。3种保护方法的防御效果及是否需要对程序进行重新编译对比结果如表1所示。

4.2性能测试

实验选用SPEC CPU2006测试集[16]进行测试。因为SecGOT未公布其相关代码或者工具,没法对其进行测试。所以这里只测试比较了Full RELRO和GOTRand运行的额外开销,结果如表2所示。通过测试集中的12个测试程序测试发现,GOTRand增加的运行开销极小,平均只有0.0029s,远小于Full RELRO的额外开销。

5结语

在现有的Linux系统中,GOT解引用和GOT覆写是两种常用的漏洞利用方法。通过GOT解引用,可以获取GOT中库函数的真正地址,从而绕过地址空间布局随机化的保护机制;通过GOT覆写,可以劫持程序的控制流程。本文提出了一种基于地址随机和段隔离的全局偏移表保护方法,通过将动态节和GOT节均加载到随机内存中,同时对全局偏移表的代码引用使用段隔离保护,很好地保护了全局偏移表中的函数指针,能够有效地缓解针对全局偏移表的漏洞利用。因为段寄存器是Intel处理器所特有的寄存器,所以该方法只适用于Intel架构的ELF程序的全局偏移表保护。下一步将其他处理器的架构进行研究,提出更加通用的保护方法。

参考文献:

[1]

TEAM PAX. PaX address space layout randomization [EB/OL]. [20030315]. https://pax.grsecurity.net/docs/aslr.txt.

[2]

Wikipedia. Data execution prevention [EB/OL]. [20150312]. https://en.wikipedia.org/wiki/Data_Execution_Prevention.

[3]

MARCOGISBERT H, RIPOLL I. On the effectiveness of fullASLR on 64bit Linux [EB/OL]. [20151120]. http://cybersecurity.upv.es/attacks/offset2lib/offset2libpaper.pdf.

[4]

ROGLIA G F, MARTIGNONI L, PALEARI R, et al. Surgically returning to randomized lib(c) [C]// ACSAC09: Proceedings of the 2009 Annual Computer Security Applications Conference. Piscataway, NJ: IEEE, 2009: 60-69.

[5]

Open Security Group. How to hijack the global offset table with pointers for root shells [EB/OL]. [20150404]. http://www.opensecurity.org/texts/6.

[6]

DAVI L, SADEGHI A R, LEHMANN D, et al. Stitching the gadgets: on the ineffectiveness of coarsegrained controlflow integrity protection [C]// SEC14: Proceedings of the 23rd USENIX Security Symposium. Berkeley, CA: USENIX Association, 2014: 401-416.

[7]

KLEIN T. A Bug Hunters Diary: a Guided Tour Through the Wilds of Software Security [M]. San Francisco: No Starch Press, 2011:183-185.

[8]

KLEIN T. RELROA (not so well known) memory corruption mitigation technique [EB/OL]. [20150221]. http://tkblog.blogspot.jp/2009/02/relronotsowellknownmemory.html.

[9]

ZHANG C, DUAN L, WEI T, et al. SecGOT: secure global offset tables in ELF executables [C]// Proceedings of the 2013 International Conference on Computer Science and Electronics Engineering. Amsterdam: Atlantis Press, 2013: 995-998.

[10]

XU J, KALBARCZYK Z, IYER R K. Transparent runtime randomization for security [C]// Proceedings of the 22nd International Symposium on Reliable Distributed Systems. Piscataway, NJ: IEEE, 2003: 260-269.

[11]

Tool Interface Standards Committee. Executable and Linkable Format (ELF) [EB/OL]. [20150401]. http://www.cs.cmu.edu/afs/cs/academic/class/15213f00/docs/elf.pdf.

[12]

ARGYROUDIS P, GLYNOS D. Protecting the core: kernel exploitation mitigations [EB/OL]. [20150517]. http://census.gr/media/bheu2011wp.pdf.

[13]

CVE Details. Vulnerability details: CVE20132028 [EB/OL]. [20130719]. http://www.cvedetails.com/cve/20132028.

[14]

MACMANUS G, HAL, SAELO. Nginx HTTP Server 1.3.91.4.0 chunked encoding stack buffer overflow [EB/OL]. [20151024]. http://www.rapid7.com/db/modules/exploit/linux/http/nginx_chunked_size.

[15]

ROEMER R, BUCHANAN E, SHACHAM H, et al. Returnoriented programming: systems, languages, and applications [J]. ACM Transactions on Information and System Security, 2012, 15(1): Article No. 2.

[16]

HENNING J L. SPEC CPU2006 benchmark descriptions [J]. ACM SIGARCH Computer Architecture News, 2006, 34(4): 1-17.

猜你喜欢

寄存器全局漏洞
中国革命战争的战略问题(节选)
漏洞在哪里
飞思卡尔单片机脉宽调制模块用法研究
侦探推理游戏(二)
移位寄存器及算术运算应用
数字电路环境下汽车控制电路信号设计
一类具有常数感染周期的传染病模型的全局稳定性分析
漏洞在哪儿
再撑一下
统筹全局的艺术