二进制分析是在机器码级别上分析二进制文件,一般不具备访问源代码所带来的好处(更多的背景信息参见本章结尾处的“参考与延伸阅读”小节)。历史上,二进制分析由一些公司在竞争的产品上进行,以理解应用的设计原理或者内部工作原理。最近,二进制分析已经成为了安全评估业界的支柱,因为它能够快速地查获软件病毒、蠕虫和其他恶意软件的功能。本小节将描述二进制分析在全知识Web应用安全评审中的角色,然后将二进制分析应用到样板Web应用的二进制文件,解说其基础知识。
表10-1 评估和改进代码安全性的工具
警告 进行二进制分析可能违反应用的最终用户许可协议(EULA),而且在某些情况下,代码的逆向工程可能形成犯罪。
二进制分析在全知识评审中的角色
在我们说明二进制分析的基本技术之前,澄清它在全知识Web应用安全评估中的作用是很重要的。
首要的问题是“如果我已经得到了源代码,为什么还要花费精力分析二进制文件呢?”许多安全研究人员发现,二进制分析对源代码评审是个强有力的补充,主要是因为二进制分析在原生部署环境中研究应用,就像应用实际运行一样。这一过程能够揭示独立查看源代码时不是很明显的许多其他问题。这些问题包括编译器对代码的修改,运行时环境引入的代码交互和变量,或者执行时才明显出现的竞争情况。
最重要的是,二进制分析能够识别第三方程序库引入的漏洞——甚至是那些用户没有源代码的程序库。在我们的咨询工作中,我们已经看到了新软件开发中使用了越来越多的外部代码。在许多情况下,这些组件的源代码都无法得到。因此,即使你是内部安全审计团队的成员,也不能确保拥有所有内部Web应用的源代码访问权,这就使得二进制分析成为审计人员工具箱的重要组成部分。
最后,要注意Web应用中编译后代码在历史上的重要性。我们在第1章中已经讲述过,Web来源于提供静态文档的技术,日益发展为提供动态、可伸缩、高性能的成熟机制。Microsoft互联网服务器应用编程接口(ISAPI)和Apache可加载模块是这种革新的最新例子。它们提供与Web服务器的编程集成,通常提供比外部通用网关接口(CGI)可执行程序快得多的应用性能。在高性能Web应用中使用ISAPI和Apache可加载模块已经很普遍;因此,我们在下一小节将使用ISAPI来说明实际的Web应用上的二进制分析。
二进制分析实例
在接下来的小节(以及本章的其他地方)中,我们将引用自己创建的一个ISAPI实例“secret.dll”。这个ISAPI的主要功能是从用户那里接收一个字符串,根据输入值显示“成功”或者“不成功”页面。secret.dll通过部署在Microsoft IIS Web服务器上的一个典型Web界面使用,因此可以通过HTTP访问,如图10-5所示。提供正确的密码就允许访问“成功”页面,否则就显示“不成功”页面。静态的密码存储在ISAPI DLL中,可以用来与用户的输入比较。本小节的目标就是说明在Windows平台上使用二进制分析获得这个密码的方法。在下面的讨论中假设secret.dll已经正常地安装和运行在一台Windows IIS机器上,而且,我们具有调试该系统的能力。
图10-5 我们的示例ISAPI DLL的Web界面
提示 如果你想跟着做,可以在http://www.webhackingexposed.com上下载Secret.dll。
调试基础
二进制分析的第一步是将目标二进制文件装入你喜欢的调试器中。在这个例子中,我们使用一个由Oleh Yuschuk编写的免费Win32调试器OllyDbg,。和Microsoft的WinDBG一样,OllyDbg是本书编写期间最易用的免费调试器之一。来自Hex-Rays的商用工具IDA Pro是另一个流行的调试套件。
图10-6展示了OllyDbg的主界面,包括CPU窗口,这时大部分调试工作发生的地方。CPU窗口包括5个面板:反汇编程序、信息、寄存器、内存转储和栈。反汇编程序面板显示在面板中选中的代码,寄存器面板解读当前选中线程的CPU寄存器内容,内存转储面板显示内存的内容,栈面板显示当前线程的栈。
图10-6 OllyDbg
可以在OllyDbg中(File|Open菜单)直接打开应用进行调试,或者将OllyDbg关联为运行应用进程(File|Attach|进程EXE名称|Attach)。在活动的应用处理输入时进行调试是逆向工程的最佳途径,所以我们对secret.dll采用这个方法。因为secret.dll是一个ISAPI,所以它在IIS Web服务器进程中运行。我们将使用OllyDbg(File|Attach|inetinfo.exe|Attach)关联到IIS主进程(inetinfo)。
关联之后,我们很快发现secret.dll包含一个IsDebuggerPresent函数,在我们试图单步执行时终止运行。这种技术常用于阻止调试,但是很容易避开。最简单的方法是装入OllyDbg的命令行插件(Alt+F1),插入如下命令:
set byte ptr ds:[fs:[30]+2]] = 0
这条命令设置IsDebuggerPresent API始终返回“false”,实际上掩盖了调试程序的存在。
还有一个办法,可以在IsDebuggerPresent函数上设置一个断点,手工将其值设置为0。这种方法需要更多的精力,在这里描述是因为它能说明一些基本的调试技术。我们首先重新装入secret.dll(用OllyDbg的Ctrl+F2快捷键),当调试程序暂停时,加载命令行插件(Alt+F1)在函数调用IsDebuggerPresent上设置一个断点(输入bp IsDebuggerPresent),如图10-7所示。
图10-7 在IsDebuggerPresent函数上设置断点
提示 插件应该显示为工具栏的一部分;如果没有,就需要设置插件路径。设置的方法是浏览到Options|Plugin path菜单,更新插件的位置(一般来说是OllyDbg的主目录)。
我们继续加载DLL(Shift+F9)直到到达IsDebuggerPresent断点(在图10-8中最上面的箭头所指处)。接着我们执行下两条指令(Shift+F7)并且在图10-8的第二个箭头上停下来。在反汇编程序面板上单击右键,选择Follow from Dump|Memory Address,内存转储面板中显示IsDebuggerPresent函数的位置和值。位置为7FFDA002,内容为
01 00 FF FF FF FF 00 00 40 00 A0 1E 19 00
右键单击字符串中的第一个值(01)选择Binary\Fill With 00's,将会把函数的结果更新为00,如图10-8中下面的两个箭头所示。
图10-8 绕过IsDebuggerPresent函数
现在我们已经人工修改了IsDebuggerPresent API的返回值,使其始终为0。因此,这个DLL现在在加载时不会因为OllyDbg的存在而终止。
二进制分析技术: 现在,我们可以开始学习二进制分析的基本要点了。我们将要使用的主要技术包括:
·枚举函数:我们将查找常与安全问题相关的函数,比如字符串API如strcpy和strcat。
·识别ASCII字符串:这些字符串包括隐藏的密码字符串,也可能指出常见的例程(可以为我们“映射”二进制文件的功能,有助于进一步分析)。
·单步运行关键功能:我们获得函数和字符串的基本清单之后,就可以单步运行二进制,在感兴趣的例程上设置断点等。这最终将揭示所有关键的安全漏洞。
首先,我们列举secret.dll使用的所有函数。回到OllyDbg中,从加载的可执行模块列表(View(查看)|Executable Modules(可执行模块))中右键单击secret.dll,选择View Names显示secret.dll使用的函数列表。这个列表包含了输入和输出的函数调用。人们可能感兴趣的一些函数包括strcpy和strcat(因为使用这些较旧函数进行字符串操作常常容易遭到缓冲区溢出攻击),还有memcpy(也有类似的问题)。这些有问题的C/C++函数都有很好的文档;只要在互联网上搜索“不安全的C/C++函数”就能得到很多好的参考。
提示 函数调用也可以使用命令行工具dumpbin.exe转储,这个工具由Visual C++提供(dumpbin/EXPORTS secret.dll)。
右键点击加载了secret.dll的反汇编程序面板,选择Search For(搜索)|All Referenced Text Strings(所有引用字符串)识别secret.dll中的ASCII字符串。
提示 “strings”工具也可用于提取secret.dll中的ASCII字符串。
最后,我们探查一些更有诱惑力的函数,更进一步地分析secret.dll的关键功能。首先,右键单击MSVCR71.strcpy,选择输入引用,弹出一个带有引用列表的新窗口,我们将在应用上设置断点(OllyDbg的F2快捷键能够很方便地设置断点)。在MSVCR71.strcat和MSVCR71.memcpy上重复同样的工作。
我们还将在反汇编程序窗口中点击右键,选择Search For|All Referenced Text Strings在ASCII字符串上设置断点。我们立刻侦查到在输出中有趣的内容:“You don抰have a valid key,The key you attempted was”(你没有有效的密钥,你所输入的密钥是……”。这可能是无效字符串输入时回显的错误信息,可能指向将密码字符串与输入比较的函数!
提示 在某些应用中,开发人员将错误信息修改为字符数组避免这种攻击,使得寻找字符串更难一些。
现在让我们来为secret.dll提供一些实际的输入,看看它所展示的。我们浏览到图10-5中的网页,输入任意字符串AAAAAAAA。OllyDbg在“Failed Secret Test”错误信息处暂停。右键单击反汇编程序面板,选择Analysis(分析)|Analyze Code(分析代码)。在分析完成之后,查看断点上的几行代码,我们注意到另一个ASCII字符串“SecurityCompass”,如图10-9所示。
进一步研究这些代码,我们注意到字符串“SecurityCompass”在与Arg2进行比较。Arg2被赋值为通过Web传递的值,并且使用EDX寄存器压入栈(内存位置1000117D)。一旦这两个值装入栈,在函数调用里就进行比较(内存位置10001183 CALL secret.10001280)。结果是更新EAX寄存器。该寄存器被设置为1或者0。如果EAX(TEST EAX,EAX)为9,转移到“失败信息”;否则,跳转到“成功信息”。因此如果在Web界面中输入“SecurityCompass”字符串,就会显示“成功信息”;否则就显示“失败信息”。中奖了!我们发现了这个Web应用的“开门咒语”。
等等——还有更多!继续执行下面几行指令(使用OllyDbg Shift+F9快捷键),执行应该在strcat断点暂停。我们将在src和dst处加入更多断点,这两处是strcat的参数。接着我们返回并且再次向应用提供任意输入,在调试器中查看执行情况。应用现在应该在src处停止,这个位置应该包含从界面传递过来的字符串“SecurityCompass”,而dst应该包含“成功信息”字符串。因此,strcat用于生成最终回显给客户的字符串。
图10-9 在secret.dll中发现了有趣的ASCII字符
我们前面已经说过,strcat是具有著名的安全问题的C/C++字符串操纵函数。例如,strcat不取任何最长长度值(和更安全的strncat不同)。因此,足够长的字符串传递给ISAPI时可能造成不正常的行为。为了确定在ISAPI可能成为问题的长度,审核strcat函数周围的代码将会得出赋予目标值的最大长度,如图10-10所示。
图10-10 跟踪strcat函数
目标使用LEA ECX,DWORD PTR SS:[EBP-98]指令装入栈内。因此,可以存储的最大值是16进制的98,也就是10进制的152(在程序里声明的空间是140个字节,剩余的字节数用于对齐)。提供超过152个字符的输入可能造成secret.dll的缓冲区溢出。152个字符还包括回显给客户端的整个页面(104个字符)。所以,发送大约152个字符长的字符串将使应用崩溃。
注意 如果禁用C++错误处理编译器选项,可能得到更详细的错误。
这里能想起来的另一种简单的攻击是跨站脚本,因为secret.dll似乎不进行任何输入净化。我们可以将如下输入发送到Web输入界面测试这种漏洞:
<script>alert('ISAPI XSS')</script>)
总地来说,进行二进制分析不仅有助于找到密码,还可以发现应用中的缺陷!