生产环境中的软件通常会存在一些漏洞,这些漏洞可能是由于糟糕的编码实践或者在软件开发生命周期里对安全的处理代码参数缺乏足够的认识造成的,最终导致漏洞被黑客利用。攻击软件同时需要安全策略能力和开发技能,但是很不幸,很多开发商本身也不具备这些能力。在本篇中,我们将展示在没有源代码的情况下怎么通过反汇编来给软件打补丁(存在缓冲区溢出漏洞)。 ※缓冲区溢出漏洞 在这类漏洞中,我们的做法是利用网络,程序控制器,输入等等,发送超大的数据缓冲区给程序,覆盖程序内存的重要部分。在这些缓冲区覆盖程序内存之后,我们可以重定向程序的执行流并运行注入代码。 首先,我们需要做的是查明程序的哪一部分可以用来重写内存。处理这个任务的过程叫作“fuzzing”。我们可以为Metasploit框架中的各种协议找到若干个fuzzer(执行fuzzing任务的工具)。 接下来这个例子中,我们用metasploit对一个ftp服务器进行fuzz: Fuzzer运行几分钟后,程序就崩溃了,见下图: 在Metasploit窗口中,我们可以看到崩溃缓冲区的长度: 在分析所有输出内容之后,我们可以得出:在ftp服务器通过用户命令发送了一个大于250的缓冲区后,程序崩溃了。 我们可以使用python来重现崩溃: 现在,我们重新实施这次攻击,但是首先要将FTP SERVER进程附加到一个调试器上,在这里我们用的调试器是OLLYDBG。 在实施攻击之后,我们可以很直观地看到ESP,EDI和EIP寄存器被覆盖。 稍微研究一下,大家可以发现:EIP控制程序的执行流,如果可以重写EIP,那么就可以手动重定向程序的执行流。EIP指向下一个待执行地址。 在这里,我们需要知道要重写的EIP缓冲区长度。我们可以在metasplpit中用pattern_create创建一种模式,并且作为一个缓冲区使用,来获取重写EIP的4个字节的位置。 把这些命令添加到我们的利用代码中,并再次运行: 现在,我们可以看到程序内存中的模式。 现在需要使用pattern_offset(偏移量模式)来找到那4个字节的准确位置(只要把4个字节作为一个脚本参数粘贴到EIP里面)。 由于在EIP之后ESP就被重写,我们可以写出这样一段利用代码如下: 并且,如果重新加载,在OLLY里面,可以看到它运行得很好。 在EIP之后,是这样改写ESP的: 那么在EIP里面我们需要做什么呢?将我们的恶意代码放到重写EIP的代码后面,然后需要做的只是简单的JMP ESP。 记住,EIP将包含下一条待执行指令的地址,所以此时需要做的是找到包含JMP ESP的地址。我们可以在OLLY(在E标签页)中进行查找。 一个简单的命令检索将会返回给我们一个地址。 现在,我们拷贝这个地址: 最后,我们需要做的是,在实行攻击之后,加入并执行我们的shell代码。我们可以用metasploit生成这些shellcode。 现在我们的利用代码如下。注意一下案例中CPU的ENDIAN,在EIP寄存器中我们会用到小端格式。 现在,如果我们再次实行攻击,将会运行我们的shellcode。 好的,现在我们可以生成另外的shellcode来执行不同的任务。 我们可以生成一段反向连接的shell代码,来访问我们的受害主机。 把这行代码我们的利用代码中, 最后运行利用代码: 我们的利用代码编写完成 。 提示:注意一些特殊字符。如果在缓冲区中间,利用代码被截断,可能是由于一些特殊字符导致的。特殊字符诸如“xa0″、 “|x00″等,会截断shellcode,你必须通过测试找到这些字符,并且在shellcode中避免用到,可别说我没提醒过你! ※有漏洞的软件 给软件打补丁是一种通过反汇编找到有问题的代码段并修改有问题代码的方法。我们的例子是一个很简单的程序,它接受用户输入的一段字符串,然后再输出到屏幕上。 但是这个程序存在一个问题,机智的黑客很容易就会发现它。这里开发者忘记验证用户的输入了,假如用户输入的字符串长度超过了预期怎么办?现在我们就输入超长的字符串,程序出现了一些异常,系统提示了一个缓冲区溢出错误并终止了。这个异常不是程序的预期结果。 现在我们已经知道程序存在缓冲区溢出漏洞了,但是我们没有源代码该怎么修复呢?当然有办法,现在已经有很多著名的逆向工程工具了,这里我们将使用IDA Pro和Ollydbg来完成任务。首先使用IDA Pro打开可执行程序,通过查看汇编代码我们很快就能发现漏洞的原因了,scanf函数没有检查输入字符串的长度(%s)。 我们已经找到要修复的代码段了(scanf),现在只需要在格式化输入字符串中加入一个整数就可以解决问题了(例如%10s)。但是我们现在不知道原来代码里存储用户输入字符串的字符数组的大小,要解决这个问题我们在Ollydbg中打开可执行程序。我们可以很轻松的发现数组的大小是16,OD同时也显示了 %s 字符串的偏移地址。 所以用%15s替换%s就可以修复这个漏洞,找到下图高亮的代码段并按下CTRL+G我们就可以直接访问地址0040401B了。 下面我们就要修改指令了AND EAX 590A0073(16进制的代码25 73 实际上就是%s)。 要在二进制代码中打上补丁,我们需要选中地址0040401B并按空格键(我们需要把%15s写到这里)。 在把%15s输入前,我们需要先把它转换为16进制。 现在我们可以用31:35:73 (%15s) 替代590A073了,但是在内存中我们需要调换一下位置,即733531,因为16进制代码在执行时总是后进先出。 最后,右击0040401B地址处,然后点击Edit标签中的“copy to executable”来永久保存我们的修改。 这时会出现一个新的窗口,关闭它的时候会提示是否保存。我们保存并为程序取一个新名字,然后在OD中再次打开这个新的程序,因为还有一些工作没有做完。一次成功的补丁不仅恩能够修复漏洞,而且程序还要能够像以前一样正常运行。这里我们修复了缓冲区漏洞,但是程序并不能正常运行,原因是我们增加的字节影响了原来的程序执行流程。CTRL+G跳转到0040401E: 这里我们可以发现JNB SHORT 00404020指令跳转出错了,它应该跳转到输出字符串的printf函数处,正确的地址是0040402D。 在0040401E处我们按下空格键并修改代码。 然后我们在0040401E处再次右键并“copy to executable”保存一个新的可执行文件。现在我们执行可执行文件,并随便输入超过16个字节的字符串,可以看到一切正常。 软件里的漏洞会被黑客以任何可能的理由利用,不管这些漏洞是有意或者无意形成的,在这篇文章中我们都学会了如果通过逆向工程在没有源代码的情况下给程序打补丁。