发生意外事件时,许多Web应用程序返回详尽的错误消息。从仅仅披露错误类型的简单内置消息到泄露许多应用程序状态细节的详细调试信息都涵盖在错误消息中。
在部署之前,大多数应用程序都接受了各种可用性测试。通常,这种测试能够发现在正常使用应用程序过程中出现的大部分错误条件。因此,一般情况下,应用程序会对这些条件进行合理地处理,而不会向用户返回任何技术消息。但是,如果应用程序正在遭受攻击,很可能就会出现各种各样的错误条件,导致应用程序向用户返回更加详细的信息。如果出现极不平常的错误条件,即使是最注重安全的应用程序(如电子银行使用的应用程序),也会向用户返回非常详细的调试消息。
如果在解释型Web脚本语言(如VBScript)中出现错误,应用程序通常会返回一条简单的错误消息,以揭示错误的本质,并且还可能会有发生错误的文件的行号。例如:
这种消息中并不包含任何与应用程序状态或被处理的数据有关的敏感信息。但是,渗透测试员可以利用它从各方面缩小攻击范围。例如,当为探查常见的漏洞在一个特殊的参数中插入各种 攻击字符串时,可能会遇到以下消息:
这条消息指出,修改的值被赋给了一个数字式参数,但是,由于提交的输入中包含非数字字符,因而不能赋给上述参数。在这种情况下,向这个参数提供非数字攻击字符串可能达不到任何目的,也不会发现各种类型的漏洞,因此,最好选择其他的参数。
此外,这种类型的错误消息还有助于更好地理解服务器端应用程序的逻辑。因为消息披露了发生错误的行号,所以能够确定两个不同的畸形请求是触发同一个错误,还是不同的错误。通过在几个参数中提交不良的输入并确认错误发生的位置,还可以确定应用程序处理不同参数的顺序。系统性地修改不同的参数,渗透测试员就可以解析出服务器执行的各种代码路径。
大多数Web应用程序用比简单脚本更复杂、但仍然在一种托管执行环境(managed execution environment)下运行的语言编写,例如,Java、C#和Visual Basic. NET。如果这些语言中出现无法处理的错误,浏览器往往会显示完整的栈追踪。
栈追踪是一种结构化的错误消息。它首先说明具体的错误,接着在后面的许多行中描述错误发生时调用栈的执行状态。调用栈的首行显示生成错误的函数,第二行显示调用前一个函数的函数,以此类推,直到显示所有被调用的函数。
下面是一个由ASP.NET应用程序生成的栈追踪:
这种错误消息提供大量有用的信息,可帮助攻击者优化针对应用程序的攻击。
通常,它会说明错误发生的准确原因。攻击者可以根据这些信息调整输入内容,避开错误条件,从而继续实施攻击。
调用栈经常会引用应用程序使用的大量库和第三方代码组件。可以查阅这些组件的文档资料,了解它们的预期行为与假设。还可以创建这些组件的本地应用,并对它进行测试,了解应用程序如何处理出人意料的输入,并确定潜在的漏洞。
调用栈中包含用于处理请求的所有权代码组件的名称。了解这些组件的命名方案及其相互关系有助于推断应用程序的内部结构与功能。
栈追踪中通常包含行号。和前面描述的简单错误消息脚本一样,可以利用这些行号探查并理解每个应用程序组件的内部逻辑。
错误消息中常常包含与应用程序及其运行环境有关的其他信息。在前面的示例中,可以确定应用程序所使用的 ASP.NET 平台的版本,因此就可以研究这个平台,查找任何已知或新出现的漏洞、反常行为、常见的配置错误等。
一些应用程序生成自定义的错误消息,其中包含大量的调试信息。在开发与测试阶段,这些消息有助于开发者对应用程序进行调试,其中常常包含大量与应用程序运行状态有关的信息。例如:
详尽的调试消息中通常包含以下信息。
可通过用户输入操纵的关键会话变量值。
数据库等后端组件的主机名称与证书。
服务器中的文件与目录名称。
嵌入在有意义的会话令牌中的信息(请参阅第7章了解相关内容)。
用于保护通过客户端传送的数据的加密密钥(请参阅第5章了解相关内容)。
在本地代码组件中出现的异常调试信息,包括CPU寄存器的值、栈的内容、加载的DLL列表及其基本地址(请参阅第16章了解相关内容)。
如果在实际的生产代码中出现这种泄露功能的错误,那么应用程序就存在严重的安全缺陷。渗透测试员应该对它进行仔细检查,确定任何可用于扩大攻击范围的数据,并且可以通过提交专门设计的输入操纵应用程序的状态,控制其获取信息的情况。
不仅应用程序自身,数据库、邮件服务器或SOAP服务器等后端组件也会返回详尽的错误消息。如果发生完全无法处理的错误,应用程序通常会返回一个HTTP 500状态码,而且响应主体中也包括其他与错误有关的信息。其他情况下,应用程序会对错误进行适当处理,并向用户返回一条定制消息,其中有时还包括后端组件生成的错误信息。在某些情况下,信息披露本身可能被攻击者当做攻击手段。应用程序通常会在调试消息或异常错误中无意披露信息,因此组织的安全规程可能会完全忽略这种信息披露。
我们将在以下几节中讲到,利用应用程序返回的消息,攻击者可以实施一系列其他攻击。
利用信息披露扩大攻击范围
在针对服务器后端组件实施特定攻击时,这些组件在遇到错误时常常会提供直接反馈。渗透测试员可以利用这些反馈对攻击进行调整。数据库错误消息中通常包含有用的信息。例如,它们通常会披露造成错误的查询,渗透测试员可以将其用于优化SQL注入攻击。
请参阅第9章了解如何实施数据库攻击,并根据错误消息提取信息的详细方法。
错误消息中的跨站点脚本攻击
如第12章所述,针对跨站点脚本的安全防御是一个艰巨的任务,需要确定用户提交的数据的每一个输出位置。虽然大多数框架在报告错误时都会对数据进行HTML编码,但并不是所有框架都这样做。错误消息常常在HTTP响应中的非常规位置多次出现。在Tomcat使用的HttpServlet-Response.sendError()调用中,错误数据还是响应消息头的一部分:
如果拥有对输入字符串Doc10083011的控制权,攻击者就可以提交换行字符并实施HTTP消息头注入攻击,或在HTTP响应中实施跨站点脚本攻击。有关详细信息,请访问以下链接:
http://www.securityfocus.com/archive/1/495021/100/0/threaded
通常,定制错误消息主要发送到控制台等非HTML目标,但有时用户可以在HTTP响应中发现这类错误显示的消息。在这些情况下,攻击者就可以轻松实施跨站点脚本攻击。
信息披露中的解密提示
我们在第11章中的示例讲到,利用应用程序意外显示的“加密提示”,可以解密以加密格式 向用户显示的字符串。信息披露也会导致同样的问题。在第7章的示例中,某应用程序提供一个用于文件访问的加密下载链接。如果某个文件已被移走或删除,应用程序会报告无法下载该文件。当然,错误消息中包含了该文件的解密值,因此,可以向该下载链接提供任何加密的“文件名”,从而导致错误。
在这种情况下,信息披露是由刻意滥用反馈造成的。如果对参数进行解密,然后将其用在各种函数中,只要其中的任何函数会记录数据或生成错误消息,则这时导致的信息披露往往更具偶发性。例如,笔者曾遇到一个复杂的工作流程应用程序,该应用程序使用通过客户端传送的加密参数。如果将dbid和grouphome的默认值互换,应用程序将返回以下错误:
这段消息提供了相当重要的信息。具体来说,dbid实际上是Oracle数据库的连接的加密SID(连接描述符采用“服务器:端口:SID”的形式),grouphome则为加密的文件路径。
在以下与许多其他信息披露攻击类似的攻击中,了解文件路径为攻击者提供了实施文件路径操纵攻击所需的信息。在文件名中提供3个路径遍历字符,并向上导航类似的目录结构,就可以直接向其他组的工作空间上传包含恶意代码的文件: