0day安全
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.3 缓冲区的组织

3.3.1 缓冲区的组成

如果选用jmp esp作为定位shellcode的跳板,那么在函数返回后要根据缓冲区大小、所需shellcode长短等实际情况灵活地布置缓冲区。送入缓冲区的数据可以分为以下几种。

(1)填充物:可以是任何值,但是一般用NOP指令对应的0x90来填充缓冲区,并把shellcode布置于其后。这样即使不能准确地跳转到shellcode的开始,只要能跳进填充区,处理器最终也能顺序执行到shellcode。

(2)淹没返回地址的数据:可以是跳转指令的地址、shellcode起始地址,甚至是一个近似的shellcode的地址。

(3)shellcode:可执行的机器代码。

在缓冲区中怎样摆放shellcode对exploit的成功至关重要。回顾2.4节的实验和3.2节实验中缓冲区分布的不同,如图3.3.1所示。

2.4节的exploit中,shellcode只有几十个字节,我们干脆把它直接放在缓冲区buffer[44]里,所以shellcode位于函数返回地址之前。

3.2节的exploit中,我们使用了跳转指令jmp esp来定位shellcode,所以在溢出时我们比2.4节中多覆盖了一片内存空间,把shellcode恰好布置在函数返回地址之后。

您会在稍后发现把shellcode布置在函数返回地址之后的好处(不用担心自身被压栈数据破坏)。但是,超过函数返回地址以后将是前栈帧数据(栈的方向,内存高址),而一个实用的shellcode往往需要几百个字节,这样大范围地破坏前栈帧数据有可能引发一些其他问题。例如,若想在执行完shellcode后通过修复寄存器的值,让函数正常返回继续执行原程序,就不能随意破坏前栈帧的数据。

图3.3.1 不同缓冲区组织方式

当缓冲区较大时,我们倾向于像2.4节中那样把shellcode布置在缓冲区内。这样做有以下好处。

(1)合理利用缓冲区,使攻击串的总长度减小:对于远程攻击,有时所有数据必须包含在一个数据包中!

(2)对程序破坏小,比较稳定:溢出基本发生在当前栈帧内,不会大范围破坏前栈帧。

当然,即便是使用跳转指令来定位shellcode,我们也可以把缓冲区布置成类似2.4节中那样。例如,图3.3.1中的最后一种组织方式,在返回地址之后再多淹没一点,并在那里布置一个仅仅几个字节的“shellcode header”,引导处理器跳转到位于缓冲区中那一大片真正的shellcode中去。

3.3.2 抬高栈顶保护shellcode

将shellcode布置在缓冲区中虽然有不少好处,但是也会产生问题。函数返回时,当前栈帧被弹出,这时缓冲区位于栈顶ESP之上的内存区域。在弹出栈帧时只是改变了ESP寄存器中的值,逻辑上,ESP以上的内存空间的数据已经作废;物理上,这些数据并没有被销毁。如果shellcode中没有压栈指令向栈中写入数据还没有太大影响;但如果使用push指令在栈中暂存数据,压栈数据很可能会破坏到shellcode本身。这个过程如图3.3.2所示。

当缓冲区相对shellcode较大时,把shellcode布置在缓冲区的“前端”(内存低址方向),这时shellcode离栈顶较远,几次压栈可能只会破坏到一些填充值nop;但是,如果缓冲区已经被shellcode占满,则shellcode离栈顶比较近,这时的情况就比较危险了。

图3.3.2 栈中的shellcode被破坏

为了使shellcode具有较强的通用性,我们通常会在shellcode一开始就大范围抬高栈顶,把shellcode“藏”在栈内,从而达到保护自身安全的目的。这个过程如图3.3.3所示。

3.3.3 使用其他跳转指令

使用jmp esp做“跳板”的方法是最简单,也是最常用的定位shellcode的方法。在实际的漏洞利用过程中,应当注意观察漏洞函数返回时所有寄存器的值。往往除了ESP之外,EAX、EBX、ESI等寄存器也会指向栈顶附近,故在选择跳转指令地址时也可以灵活一些,除了jmp esp之外,mov eax、esp和jmp eax等指令序列也可以完成进入栈区的功能。

图3.3.3 抬高栈顶以保护shellcode

这里给出常用跳转指令与机器码的对应关系,如表3-3-1所示。

表3-3-1 常用跳转指令与机器码的对应关系

您可以在3.2节中给出的jmp esp指令地址搜索程序的基础上稍加修改,方便地搜索出其他跳转指令的地址。

3.3.4 不使用跳转指令

个别有苛刻的限制条件的漏洞不允许我们使用跳转指令精确定位shellcode,而使用shellcode的静态地址来覆盖又不够准确,这时我们可以做一个折中:如果能够淹没大片的内存区域,可以将shellcode布置在一大段nop之后。这时定位shellcode时,只要能跳进这一大片nop中,shellcode就可以最终得到执行,如图3.3.4所示。这种方法好像蒙着眼睛射击,如果靶子无比大,那么枪枪命中也不是没有可能。

在浏览器漏洞利用时,常常采取的Heap Spray技术用的就是上述的缓冲区分布思想。Heap Spary技术会在后续的章节及案例中详细讨论。

3.3.5 函数返回地址移位

在一些情况下,返回地址距离缓冲区的偏移量是不确定的,这时我们也可以采取前面介绍过的增加“靶子面积”的方法来提高exploit的成功率。

如果函数返回地址的偏移按双字(DWORD)不定,可以用一片连续的跳转指令的地址来覆盖函数返回地址,只要其中有一个能够成功覆盖,shellcode就可以得到执行。这个过程如图3.3.5所示。

图3.3.4 扩大shellcode面积,提高命中概率

图3.3.5 大面积“扫射”返回地址

还有一些情况会更加棘手。考虑由strcat产生的漏洞。

……
strcat(“程序安装目录”,输入字符串);
……

而不同的主机可能会有不同的程序安装目录。例如:

c:\failwest\
c:\failwestq\
c:\failwestqq\
c:\failwestqqq\

这样,函数返回地址距离我们输入的字符串的偏移在不同的计算机上就有可能按照字节错位,如图3.3.6所示。

图3.3.6 按字节错位引起的定位失败

图3.3.6中本想把函数返回地址覆盖为0x7C81CDDA处的跳转地址,在本机调试通过后,有可能会由于其他计算机上作为字符串前半部分的程序安装目录不同,而使覆盖的地址错位失效。这样,我们精心设计的exploit在别的计算机上可能只有1/4的成功率,通用性大大降低。

解决这种尴尬情况的一个办法是使用按字节相同的双字跳转地址,甚至可以使用堆中的地址,然后想办法将shellcode用堆扩展的办法放置在相应的区域。这种heap spray的办法在IE漏洞的利用中经常使用,如图3.3.7所示。

图3.3.7 用heap spray部署技术解决字节错位问题

我们将在第27章中的IE漏洞利用实验中实践这种方法。