在听到术语“渗透测试”时,您可能会联想到一个孤独的天才正在对一些倒霉的软件执行神奇的测试。在渗透测试复兴之前,它或许是个真实的场景。现在,渗透测试的执行方式要系统化得多。这样做是必要的,因为安全开发生命周期 (SDL) 以及前载式安全设计和开发重点降低了潜在缺陷的数量,从而使在测试过程中查找漏洞的任务变得更加困难。软件安全测试非常重要,不能仅依靠一小组专家来完成。它必须是能够传授、系统化且可重复的过程,这样才能将其应用到各种情况中。
这并不是说渗透测试是一门科学。所有测试均有值行研究的方面;测试人员应用输入,而这些输入会更改软件内部的数据,从而导致其做出各种反应。由于复杂性很高,无法进行精确预测;但是,我们可事先做好各种规划工作,本专栏将介绍我们在 Microsoft 是如何制定规划的。
规划
对于传统测试而言,规范、用户文档、用例和其他设计文档非常充足。可使用这些信息设计一组测试用例来确认指定的功能。但有关规划的可用信息资源则非常有限。渗透测试并不是为了确认功能;其目的是确认不再存在不安全的功能。遗憾的是,没有人定义过用于此类行为的软件开发项目,因此测试人员不得不自行解决。
收集渗透测试信息的首个位置是软件与外部环境之间的接口。用户界面、网络接口、API 和处理输入的任意其他位置都是明显的黑客攻击目标。如果任意一个接口的设计或实现欠佳,它们可能会允许恶意伪造的输入并带来灾难。可通过确定和记录这些接口来开始执行渗透测试。
需要特别关注的第二个区域是错误消息和用户警告对话框(它们将把来自软件的信息传递给外部用户)。由于某些用户可能具有恶意企图,因此必须了解会向其显示哪些信息以及此类信息的传递方式。
最后,渗透测试人员通常会定义灾难场景,以此指出成功的攻击可能产生的后果。这些误用案例(或者说滥用案例)通常起源于某种威胁模型或先前已知的攻击。
对于渗透测试而言,从这三类资源中收集信息是至关重要的准备工作,并且有助于指导您完成实际的测试。
渗透测试类型
测试的对象是变化 — 找出软件及环境中可能发生变化的部分,改变它们,然后观察软件的反应。测试目标是确保软件在合理甚至不合理的生产环境中可靠且安全地运行。因此,测试人员可制定的最基本规划是了解哪些部分可能发生变化以及在测试中需使用哪些方法来分阶段实现这些变化。
从安全角度来看,环境、用户输入以及内部数据和逻辑是此类变化可能暴露出安全问题的主要位置。环境包括文件、应用程序、系统资源和应用程序使用的其他本地或网络资源。所有这些都可能成为攻击的入口点。用户输入是源自软件分析和使用的外部(通常不受信任)实体的数据。内部数据和逻辑是内部存储的变量和逻辑路径(它们具有多种可能的枚举)。
通过改变软件环境、输入域和数据/逻辑路径中的信息,就可以执行攻击。接下来我将更详细地介绍这三类攻击。
环境攻击
软件无法孤立地执行。它依赖于一些二进制和等效于代码的模块(如脚本和插件)。它还可能使用注册表或文件系统中的配置信息以及可能位于任意位置的数据库和服务。每个此类环境交互都可能成为安全漏洞的来源,因此必须对其进行测试。
对于应用程序在此类交互中所具有的信任程度,还有许多必须考虑的重要问题,其中包括:应用程序对于本地环境和远程资源的信任程度如何?应用程序是否将敏感信息放在其他应用程序可读取的资源(如注册表)中?它是否信任自身加载的每个文件或库,甚至无需确认内容?攻击者是否能够利用这种信任来强制应用程序服从其命令?
除信任问题外,渗透测试人员还应监视可能出现错误或已被攻击者替换(或修改)的 DLL、二进制代码或应用程序将与之进行交互并且未受到访问控制列表 (ACL) 完全保护或者根本未受到保护的文件。测试人员还必须监视访问共享内存资源或者在注册表或临时文件中存储敏感数据的其他应用程序。最后,测试人员必须考虑造成系统压力的因素(如网速较慢、内存不足等),并确定这些因素对安全功能的影响。
环境攻击方式通常是侵入不安全的环境并随后在该环境中执行应用程序来查看其反应。这是一种间接形式的测试;攻击者对应用程序的运行环境发起攻击。现在,我们来看直接测试。
输入攻击
在渗透测试中,来自不受信任来源的输入最为重要。包括通信路径(如网络协议和套接字)、公开的远程功能(如 DCOM、远程过程调用 (RPC) 和 Web 服务)、数据文件(二进制文件或文本文件)、执行过程中创建的临时文件以及控制文件(如脚本和 XML),所有这些内容都可能遭到篡改。最后,还必须检查允许直接用户输入的 UI 控件(包括登录屏幕、Web 前端及类似控件)。
具体来说,您需要确定是否已正确控制输入:是否接收合格的输入并拒绝不适当的输入(如长字符串、格式不正确的数据包等)?适当的输入检查和文件分析至关重要。
需执行测试来查看能不能将危险的输入项输入到 UI 控件中,并了解此时会产生什么样的后果。此类输入包括特殊字符、编码输入、脚本段、格式字符串、转义序列等。需确定嵌入到数据包字段或文件中且可能导致内存溢出的长字符串能否通过测试。协议流中损坏的数据包也是个问题。必须监视崩溃和挂起并检查堆栈是否存在可利用的内存损坏。最后,必须确保当正确防御不适当的输入时,验证和出错消息之类的信息会出现在正确的位置(客户端而非服务器端)。
输入攻击实际上类似于向应用程序扔手榴弹。其中一些能正确躲避,而另一些则会导致软件崩溃。渗透团队将负责确定各种输入攻击,并启动适当的修复措施。
数据和逻辑攻击
一些错误深藏在应用程序的内部数据存储机制和算法逻辑当中。在这种情况下,似乎存在设计和编码错误,开发人员假定用户始终会按照正确的方式执行操作或忽略了用户可能用到的某些代码路径。
拒绝服务是此类攻击的主要代表,但还不是最危险的攻击。如果开发人员没有针对大量用户(或连接、文件或导致资源耗尽的任意输入)做出计划,拒绝服务攻击就可能成功。然而,还有大量需要测试的更加隐匿的逻辑缺陷。例如,如果导致错误消息的输入和其他生成的输出向攻击者显示了可利用的信息,就可能发生信息泄漏。始终应删除此类数据,其中一个实际的示例是硬编码的测试帐户或测试 API(通常包含在内部版本中以帮助实现测试自动化)。它们可能为攻击者提供轻松的入侵点。还应执行另外两个测试:输入错误的凭据来确定内部身份验证机制是否可靠,以及选择代码路径不同的输入。通常一个代码路径是安全的,但可使用另一方式访问相同的功能(可能无意间忽略一些关键的检查)。
不要胆怯
渗透测试与传统的功能测试存在很大差异;渗透测试人员不仅缺乏适当的文档,而且还必须能够站在试图进行破坏的用户角度进行思考。这一点非常重要 — 开发人员往往假定用户都非常守规矩并且不会执行特殊的操作,因而拒绝修复错误。但实际上,您不可能如此冒险。黑客们会深入发掘各种漏洞,任意技巧、欺骗或异常测试用例都不例外。对于渗透测试人员而言,也同样适用。
====================================分割线================================
最新内容请见作者的GitHub页:http://qaseven.github.io/