空指针漏洞防御技术(初级篇)

转载自http://www.codeceo.com/article/null-pointer-01.htmlhtml

 

安全历史上因为空指针所带来的漏洞及攻击数不胜数,但因为其对利用者的编程能力有要求,对分析及防御者来讲有更高的要求,因此国内对空指针漏洞及相 关技术的讨论不是不少。今天这篇《空指针漏洞防御技术》,由绿盟科技威胁响应安全专家坐堂讲解,你们能够从中了解空指针漏洞的基础知识,并结合 Windows 8的内存防御机制实例,动手实践空指针漏洞的防御技术。编程

1 背景

指针对于绝大部分的编程人员来讲都不陌生,提及C/C++中指针的使用既带来了编程方面的方便;同时对编程人员来讲,也是对我的编程能力的一种考验,不正确的使用指针会直接致使程序崩溃,而若是是内核代码中对指针的错误使用,会致使系统崩溃,后果也是至关严重。windows

通常状况下咱们使用指针时,错误用法集中在三个方面:安全

  1. 由指针指向的一块动态内存,在利用完后,没有释放内存,致使内存泄露
  2. 野指针(悬浮指针)的使用,在指针指向的内存空间使用完释放后,指针指向的内存空间已经归还给了操做系统,此时的指针成为野指针,在没有对野指针作处理的状况下,有可能对该指针再次利用致使指针引用错误而程序崩溃。
  3. Null Pointer空指针的引用,对于空指针的错误引用每每是因为在引用以前没有对空指针作判断,就直接使用空指针,还有可能把空指针做为一个对象来使用,间接使用对象中的属性或是方法,而引发程序崩溃,空指针的错误使用常见于系统、服务、软件漏洞方面。

对于第一和第二种状况,咱们能够经过一些代码审计工具在发布以前就能肯定致使内存泄露或是野指针存在的地方。好比常见的工具备fority,valgrind等及时发现指针错误引用致使的问题。服务器

对于第三种状况,空指针(Null Pointer)引用致使的错误,依靠代码审计工具很难发现其中的错误,由于空指针的引用通常不会发生在出现空指针而后直接使用空指针状况。每每是因为代 码逻辑比较复杂空指针引用的位置会比较远,不容易发现;而且在正常状况下不会触发,只有在特定输入条件下才会引起空指针引用。对于排查此类错误也就更加困 难。网络

本文不会重点讨论内存泄露和野指针的内容,而是经过一些现有的漏洞和实例来分析一下Null Pointer 空指针。从NULL Pointer概念、本质结合静态逆向及内核动态调试技术来了解在win7 32位和win8 32位下系统对Null Pointer处理状况有什么不一样;Win8 32位针对Null Pointer添加了哪些防御机制。函数

本文从浅入深,按部就班的讲述了NULL Pointer,结合静态逆向分析和内核级动态调试技术深刻剖析win8系统对零页内存的保护机制,其中还涉及到了一些内核调试的技巧,对于对NULL Pointer的概念、使用比较模糊的人员值得一读,对于从事安全研究和学习的人员也是巩固、加深、拓展的好素材。工具

2 空指针由Null Pointer引起的漏洞

首先来看看近年来因为空指针的错误使用致使的系统、服务漏洞:学习

Microsoft windows kernel ‘win32k.sys’本地权限提高漏洞(CVE-2015-1721)(MS15-061)字体

该漏洞影响了windows Server 2003 SP2和R2 SP2,Windows Vista SP2, Windows Server 2008 SP2 和R2 SP1,Windows7 SP1,Windows8 ,Windows8.1,Windows Server2012 Gold和R2,Windows RT Gold and 8.1 。利用内核驱动程序 win32k.sys漏洞能够提高权限或是引发拒绝访问服务,主要缘由是空指针的错误引用致使。

PHP空指针引用限制绕过漏洞(CVE-2015-3411)

PHP存在安全漏洞,因为程序多个扩展中缺乏路径或某些函数的路径参数的空字节检查,容许远程攻击者利用漏洞可绕过目标文件系统访问限制,访问任意文件。

0rg空指针引用拒绝访问漏洞(CVE-2008-0153 )

X.Org是X.Org基金会运做的一个对X Window系统的官方参考实现,是开源的自由软件。libXfont是一个用于服务器和实用程序的X字体处理库。 X.Org libXfont 1.4.9以前版本和1.5.1以前1.5.x版本的bitmap/bdfread.c文件中的’bdfReadCharacters’函数存在安全漏 洞,该漏洞源于程序未能正确处理不能读取的字符位图。远程攻击者可借助特制的BDF字体文件利用该漏洞形成拒绝服务(空指针逆向引用和崩溃),执行任意代 码。

Paragma TelnetServer空指针引用拒绝服务漏洞(BID-27143)

Pragma TelnetServer是一款远程访问和控制Telnet服务器。Pragma TelnetServer处理协议数据时存在漏洞,远程攻击者可能利用此漏洞致使服务器不可用。TelnetServer服务器对每一个入站链接启动一个 telnetd.exe进程,该进程在处理TELOPT PRAGMA LOGON telnet选项(138号)期间存在空指针引用,致使进程终止。尽管终止单个进程不会影响其余进程,但终止某些进程会致使拒绝访问服务器。

OpenSSL SSLv2客户端空指针引用拒绝服务漏洞(CVE-2006-4343)

OpenSSL是一种开放源码的SSL实现,用来实现网络通讯的高强度加密,如今被普遍地用于各类网络应用程序中。OpenSSL的协议实如今处理 链接请求时存在问题,远程攻击者可能利用此漏洞致使服务器拒绝服务。SSLv2客户端的get_server_hello()函数没有正确地检查空指针。 使用OpenSSL的受影响客户端若是建立了到恶意服务器的SSLv2链接,就会致使崩溃。

Linux Kernel空指针间接引用本地拒绝服务漏洞(CVE-2014-2678)

Linux kernel 3.14版本内,net/rds/iw.c中的函数rds_iw_laddr_check在实现上存在本地拒绝服务漏洞,本地用户经过盲系统调用没有RDS传输的系统上的RDS套接字,利用此漏洞可形成空指针间接引用和系统崩溃。

ISC BIND named拒绝服务漏洞(CVE-2015-5477)

ISC BIND 9.9.7-P2以前版本、9.10.2-P3以前版本,named存在安全漏洞,远程攻击者经过TKEY查询,利用此漏洞可形成拒绝服务(REQUIRE断言失败及程序退出,指针未初始化)。

此类漏洞还有不少,从给出的几个漏洞来看,空指针漏洞主要是以拒绝服务访问漏洞为主,空指针错误引用天然会到底程序出现错误,严重者会崩溃,从而引发拒绝服务访问。

3 基础篇:空指针验证方式

由空指针的错误引用致使的漏洞,其原理自己很简单:错误引用空指针,致使非法访问内存地址。

那到底什么是空指针漏洞呢?在计算机编程过程当中使用指针时有两个重要的概念:空指针和野指针。那么在前面咱们提到的空指针漏洞是否是就是咱们在编程时所说的空指针呢?要弄清这个问题,首先须要知道编程领域中空指针和野指针分别是什么,还要弄清楚什么是空指针漏洞。

3.1 概念性验证

假如 char p,那么p是一个指针变量,该变量尚未指向任何内存空间,若是p = 0; p = 0L; p = ”; p = 3 – 3; p = 0 * 5; 中的任何一种赋值操做以后(对于 C 来讲还能够是 p = (void)0;), p 都成为一个空指针,由系统保证空指针不指向任何实际的对象或者函数。反过来讲,任何对象或者函数的地址都不多是空指针。(好比这里的(void)0就是一个空指针。固然除了上面的各类赋值方式以外,还能够用 p = NULL; 来使 p 成为一个空指针。由于在不少系统中#define NULL (void)0。

3.1.1 内存管理之空指针

空指针指向了内存的什么地方呢?标准中并无对空指针指向内存中的什么地方这一个问题做出规定,也就是说用哪一个具体的地址值(0×0 地址仍是某一特定地址)表示空指针取决于系统的实现。咱们常见的 空指针通常指向 0 地址,即空指针的内部用全 0来表示 (zero null pointer,零空指针)。

对于NULL能表示空指针,是否还有其余值来表示空指针呢?在windows核心编程第五版的windows内存结构一章中,表13-1有提到 NULL指针分配的分区,其范围是从0×00000000到0x0000FFFF。这段空间是空闲的,对于空闲的空间而言,没有相应的物理存储器与之相对 应,因此对这段空间来讲,任何读写操做都是会引发异常的。

从图中看出,对NULL指针分配的区域有0×10000之多,为何分配如此大的空间?在定义NULL的时候,只使用了 0×00000000这么一个值,而在表13-1有提到NULL指针分配的分区包含了0×00000000-0x0000FFFF,是否是有点浪费空间 了;这是和操做系统地址空间的分配粒度相关的,windows X86的默认分配粒度是64KB,为了达到对齐,空间地址须要从0×00010000开始分配,故空指针的区间范围有那么大。

3.1.2知识拓展之野指针

“野指针”又叫”悬浮指针”不是NULL指针,是指向”垃圾”内存的指针。

“野指针”的成因主要有三种:

1)指针变量没有被初始化。任何指针变量刚被建立时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。因此,指针变量在建立的同时应当 被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如: char *p = NULL; char *str = (char *) malloc(100);

2)指针p被free或者delete以后,没有置为NULL,让人误觉得p是个合法的指针。

free和delete只是把指针所指的内存给释放掉,但并无把指针自己干掉。free之后其地址仍然不变(非NULL),只是该地址对应的内存 变成垃圾内存,p成了”野指针”。若是此时不把p设置为NULL,会让人误觉得p是个合法的指针。若是程序比较长,咱们有时记不住p所指的内存是否已经被 释放,在继续使用p以前,一般会用语句if (p != NULL)进行防错处理。很遗憾,此时if语句起不到防错做用,由于即使p不是NULL指针,它也不指向合法的内存块。

用free或delete释放了内存以后,就应当即将指针设置为NULL,防止产生”野指针”。内存被释放了,并不表示指针会消亡或者成了NULL指针。

3)指针操做超越了变量的做用范围。例如不要返回指向栈内存的指针或引用,由于栈内存在函数结束时会被释放,这种状况让人防不胜防,示例程序以下:

另外须要注意的是若是程序定义了一个指针,就必需要当即让它指向一个咱们设定的空间或者把它设为NULL,若是没有这么作,那么这个指针里的内容是 不可预知的,即不知道它指向内存中的哪一个空间(即野指针),它有可能指向的是一个空白的内存区域,可能指向的是已经受保护的区域,甚至可能指向系统的关键 内存,若是是那样就糟了,也许咱们后面不当心对指针进行操做就有可能让系统出现紊乱,死机了。因此必须设定一个空间让指针指向它,或者把指针设为 NULL。

3.2源码级验证

接下来就结合两个小的实验例子看看空指针,及使用空指针的后果。

3.2.1指针使用前期对比

本例是要验证指针变量在初始化先后的情况,下图是一段代码:

代码很简单只是验证指针变量在赋值与未赋值分别指向的空间。代码中要打印的值:

  1. 指针变量初始化以前的地址
  2. 指针变量初始化以前的值
  3. 指针变量初始化以后的地址
  4. 指针变量初始化以后的值

编译以后,运行三次程序看运行的结果:

从三次运行结果来看:

  1. 每次打印指针变量的地址都不一样,由于每次运行程序,P指针变量的地址都在变更。
  2. 同一次打印中指针变量在初始化以前与初始化以后的地址没有变化。由于指针变量也是变量,变量在其生命周期中值能够变更,可是地址不会改变。
  3. 每次打印中指针变量在初始化以前的值发生了变化。指针变量的值自己也是指向一块内存地址,在初始化以前该变量不指向任何内存地址,因此该变量的值就是一个随机值(也就是指向随机内存地址)。
  4. 每次打印中指针变量在初始化以后的值没有发生变化,在指针变量初始化以后P=0,因此在内存初始化的指针变量指向了内存地址都为0×00000000的位置。在每次打印中指针变量在初始化以后的值就不会发生变化。

3.2.2指针使用后期对比

针对指针变量在使用完毕后,内存释放以后的状况对比,其源码以下:

代码没什么复杂逻辑,只是想验证一下在指针变量释放后, P=NULL以前的野指针与P=NULL以后的指针情况,打印的内容:

  1. 指针变量P开始的地址与值
  2. 指针变量P在分配内存空间以后的地址与值
  3. 指针变量P在释放内存空间以后的地址与值
  4. 指针变量P在P=NULL以后的地址与值

下图是在编译以后运行的结果:

a. 指针变量在声明以后一直到最后的程序结束,指针变量的地址一直没有变更为0x35f778。 b. 定义指针变量时其值为0×00000000 c. 在申请新的内存以后,指针变量的值为申请内存的地址0x6e7a78 d. 在内存释放后,P变成野指针,可是此时P的值仍然是申请的内存地址0x6e7a78,可是此时P指针已经不能再使用。 e. 在P=NULL,p从新指向了地址0×00000000处。

因而可知,咱们在释放指针变量后,指针变量会变成野指针,若是此时引用该指针会出现非法访问,所以须要在释放指针变量后将指针变量指向空。

3.3可视化内存验证

为了可以更加直观的查看指针变量在内存中的变化,下面就结合动态调试的技术看看指针变量在整个使用过程当中的变化状况。下面是一段程序代码:

在这里,咱们使用OD动态调试器,来看看调用printf函数的状况:

a. 第一条打印语句

printf("p address :%p, value :%08x\n", &p, p);

在调用printf前其内存状况:

可知指针变量P的值为EAX=[EBP-4],P的地址ECX=EBP-4,此时的寄存器值:

调用printf以后的值恰好是这两个值

b.第二条打印语句printf(“p intialized address :%p, value :%08x\n”, &p, p);在调用printf前其内存状况:

可知指针变量P的值为EDX=[EBP-4],P的地址EAX=EBP-4,此时的寄存器值:

image013

调用printf以后的值恰好是这两个值:

c. 第三条打印语句printf(“p free address :%p, value :%08x\n”, &p, p);在调用printf前其内存状况:

可知指针变量P的值为EAX=[EBP-4],P的地址ECX=EBP-4,此时的寄存器值:

image016

调用printf以后的值恰好是这两个值:

d. 第四条打印语句printf(“p ultimate address :%p, value :%08x\n”, &p, p);在调用printf前其内存状况:

可知指针变量P的值为EDX=[EBP-4],P的地址EAX=EBP-4,此时的寄存器值:

调用printf以后的值恰好是这两个值:

3.4 概念总结之空指针漏洞

前面对什么是空指针,什么是野指针作了讲解和验证。

在编程领域的空指针是指向NULL的指针,也就是说指向零页内存的指针叫空指针。对于未初始化的指针,释放内存而未将指针置为NULL和指针指向超 出范围的状况称为野指针。那么在第二节中列举的空指针漏洞及未列举的空指针漏洞是否是都是因为引用零页内存致使的呢(好比CVE-2014-2678)? 其实否则,有些漏洞是因为引用未初始化的指针或是引用超出范围的指针所致使,而这类漏洞应该说是因为错误的引用了野指针。好比最新的BIND漏洞 (CVE-2015-5477):

可是到目前为止还没用据说过哪一个漏洞命名为野指针漏洞,而更多的是空指针漏洞。也就是说在计算机安全领域中由空指针或是野指针致使的漏洞统一叫作空指针漏洞。

相关文章
相关标签/搜索