攻击访问控制

从逻辑上讲,应用程序核心安全机制的访问控制建立在验证和会话管理之上。到现在为止,我们已经了解了应用程序如何首先核实用户的身份,然后确认它收到的某个特殊的请求序列由该用户提出。应用程序之所以需要这样做,至少从安全上讲,是因为它必须决定是否允许某个请求执行特定的操作或访问它请求的资源。访问控制是应用程序的一个重要防御机制,因为它们负责做出这些关键决定。如果访问控制存在缺陷,攻击者往往能够攻破整个应用程序,控制其管理功能并访问属于其他用户的敏感数据。

如第1章所述,不完善的访问控制中最常见的Web应用程序漏洞,影响了我们最近测试的71%的应用程序。我们发现应用程序做出一切努力执行稳定的验证与会话管理机制,但由于没有在它们上面建立任何有效的访问控制,因而浪费了这方面的投资,这种情况非常常见。这些漏洞如此普遍的一个原因在于,需要对每一个请求,以及特殊用户在特定时刻尝试对资源执行的每一项操作执行访问控制检查。而且,与许多其他类型的控制不同,这一设计决策需要由人做出,而无法采用技术来解决。

访问控制漏洞的概念非常简单:应用程序允许攻击者执行某种攻击者没有资格执行的操作。各种漏洞之间的差异实际上可归结为这些核心漏洞表现方式上的不同,以及检测它们所需要使用的技巧之间的差异。我们将描述所有这些技巧,讨论如何利用应用程序的各种不同行为,执行未授权操作并访问受保护的数据。

常见漏洞

访问控制可分为三大类:垂直访问控制、水平访问控制和上下文相关的访问控制。

垂直访问控制允许各种类型的用户访问应用程序的不同功能。在最简单的情况下,应用程序通过这种控制界定普通用户和管理员。在更加复杂的情况下,垂直访问控制可能需要界定允许其访问特殊功能的各种不同类型的用户,给每个用户分配一个单独的角色,或一组不同的角色。

水平访问控制允许用户访问一组相同类型的、内容极其广泛的资源。例如,Web邮件应用程序允许访问自己而非他人的电子邮件;电子银行只允许转移自己账户内的资金;工作流程应用程序允许更新分配给你的任务,但只能阅读分配给他人的任务。

上下文相关的访问控制可确保基于应用程序当前的状态,将用户访问仅限于所允许的内容。例如,如果在某个过程中,用户需要完成多个阶段的操作,上下文相关的访问控制可以防止用户不按规定的顺序访问这些阶段。

许多时候,垂直与水平访问控制相互交叠。例如,企业资源规划应用程序允许每个应付账会计文员支付某一个组织单元、而非其他单元的发票,但允许应付账经理支付任何单元的发票。同样,会计文员只能支付小额发票,而大额支票必须由经理支付。财务总监可以查看公司每个组织单元的发票支付和收据,但不得支付任何发票。

如果用户能够访问他无权访问的功能或资源,就表示访问控制存在缺陷。主要有三种类型的以访问控制为目标的攻击,分别与三种访问控制相对应。

如果一名用户能够执行某项功能,但分配给他的角色并不具有这种权限,就表示出现垂直权限提升漏洞。例如,如果一名普通用户能够执行管理功能,或者一位会计文员能够支付任何金额的发票,就表示访问控制并不完善。

如果一名用户能够查看或修改他没有资格查看或修改的资源,就表示出现水平权限提升漏洞。例如,如果用户能使用Web邮件应用程序阅读他人的电子邮件,或者如果一位会计文员可以处理自己所属组织单元以外的单元的发票,那么访问控制也不完善。

如果用户可以利用应用程序状态机中的漏洞获得关键资源的访问权限,就表示出现业务逻辑漏洞。例如,用户能够避开购物结算序列中的支付步骤。

许多时候,应用程序水平权限划分中存在的漏洞可能会立即引起垂直权限提升攻击。例如,如果一名用户能够以某种方式设置其他用户的密码,那么该用户就能攻击管理员的账户并控制整个应用程序。

在我们已经描述的示例中,不完善的访问控制使获得某种用户权限的攻击者能够执行未授权操作或访问未授权数据。但是,在最严重的情况下,不完善的访问控制可能允许完全未获授权的用户访问只有特权用户才能访问的功能或数据。

完全不受保护的功能

在许多的访问控制不完善情况下,敏感功能和数据可被任何知道相关URL的用户访问。例如,在许多应用程序中,任何人只需访问一个特定的URL就能够完全控制它的管理功能:

在这种情况下,应用程序通常仅实施如下访问控制:以管理员身份登录的用户在他们的用户界面上看到一个该URL的链接,而其他用户则无法看到这个链接。这种细微的差别是应用程序用于“防止”敏感功能被未授权使用的唯一机制。

有时候,允许用户访问强大功能的URL可能很难猜测,甚至可能相当隐秘,例如:

这种情况下,开发者假设攻击者无法知道或发现这个URL,管理功能就会因此受到保护。当然,局外人很难攻破一个应用程序,因为他们不太可能猜测出实现这种目的的URL。

 错误观点 “低权限用户并不知道那个URL。我们并没有在应用程序中引用它。”

在前面的示例中,无论URL多么容易猜测,不存在任何真正的访问控制仍然等同于一个严重的漏洞。不管是在应用程序还是在用户手中,URL都不具有保密性。它们显示在屏幕上,出现在浏览器历史记录与Web服务器和代理服务器的日志中。用户可能会记下它们,以它们为书签或通过电子邮件将其四处传播。与密码不同,它们一般不需要定期修改。当用户的工作职位发生改变、需要收回他们的管理权限时,我们并没有办法从他们的记忆中删除某个特殊的URL。

一些应用程序的敏感功能隐藏在各种不太容易猜测的URL之后,但攻击者通过仔细检查客户端代码仍能发现这些URL。许多应用程序使用JavaScript在客户端动态建立用户界面。它一般建立各种与用户状态有关的标记,然后根据这些标记在用户界面(UI)中增加不同的元素。例如:

在这个示例中,攻击者只需检查JavaScript代码就可确定具备管理功能的URL,并尝试访问它们。在其他情况下,HTML注释中可能包含屏幕显示内容中没有链接的URL的引用或线索。请参阅第4章了解攻击者收集应用程序中隐藏内容信息时使用的各种技巧。

直接访问方法

如果应用程序披露实际用于远程调用API方法的URL或参数(通常它们由Java界面披露),这时可能出现功能不受保护的特例。在将服务器端代码移至浏览器扩展组件,并创建方法存根以便代码仍然能够调用它正常运行所需的服务器端方法时,往往会发生这种特例。除以上情形外,如果URL或参数使用getBalance和isExpired等标准Java命名约定,这时也可能会出现直接调用方法的情况。

原则上,与指定服务器端脚本或其他资源的请求相比,并不需要完全确保指定要执行的服务器端API的请求的安全。但实际上,这种机制往往包含漏洞。通常,客户端直接与服务器端API方法交互,并避开应用程序的正常访问控制或意外输入向量。如果其他功能从不由Web应用程序客户端直接调用,则这些功能也可以通过上述方法调用,并不受任何控制的保护。一般情况下,用户只需要能够访问某些特定的方法,但他们却拥有访问所有方法的权限。出现这种情况,或者是因为开发者并不了解用户到底需要哪些方法,因而向他们提供所有方法的访问权限;或者是因为将用户映射到HTTP服务器的API默认提供访问所有方法的权限。

以下示例显示了如何从接口securityCheck中调用getCurrentUserRoles方法:

在此示例中,除了测试对getCurrentUserRoles方法的访问控制外,还应检查是否存在其他类似命名的方法,如getAllUserRoles、getAllRoles、getAllUsers和getCurrentUserPermissions。我们将在本章后面部分进一步讨论如何测试直接访问方法的情况。

基于标识符的功能

当应用程序使用一项功能访问某个特殊的资源时,被请求资源的标识符常常以请求参数的形式、在URL查询字符串或POST请求主体中提交给服务器。例如,应用程序可能使用下面的URL显示一份属于某个用户的特殊文档:

拥有这份文档的用户登录后,这个URL的链接将会在该用户的“我的文档”页面显示。其他用户无法看到这个链接。但是,如果访问控制不完善,那么请求相应URL的任何用户都能够像授权用户那样查看这份文档。

 提示 当主应用程序连接一个外部系统或后端组件时通常会出现这类漏洞。可能很难在使用各种技术的不同系统之间共享一个基于会话的安全模型。面对这种问题,开发者往往会避免使用那种模型,而使用客户端提交的参数做出访问控制决定。

在这个示例中,寻求获得未授权访问的攻击者不仅需要知道应用程序页面的名称(View-Document.php),而且需要知道他想要查看的文档的标识符。有时,应用程序生成的资源标识符非常难以预测,例如,它们可能是随机选择的GUID(Global Unique Identifier,全局统一标识符)。在其他情况下,它们可能很容易猜测,例如,它们可能是连续生成的数字。但是,无论是哪一种情况,应用程序都易于遭受攻击。如前所述,URL并不具有保密性,资源标识符也同样如此。通常,希望发现其他用户资源标识符的攻击者可在应用程序的某个位置找到这些信息,例如访问日志中。即使在应用程序的资源标识符很难猜测的情况下,如果没有对那些资源实施合理的访问控制,它们仍然易于受到攻击。如果标识符很容易预测,问题就会更加严重,也更容易被攻击者所利用。

 提示 应用程序日志通常是一个信息金矿,其中包含大量可被用作标识符的数据项,可利用它们探查通过标识符访问的功能。应用程序日志中常见的标识符包括:用户名、用户ID、账号、文档ID、用户群组与角色以及电子邮件地址。

 注解 除用于指代应用程序中基于数据的资源外,这种标识符还常用于表示应用程序功能。如第4章所述,应用程序可以通过单独一个页面提供各种功能,它接受一个功能名称或标识符为参数。同样,在这种情况下,应用程序也只是在各种类型的用户界面中显示或隐藏一个特殊的URL,实施肤浅的访问控制。如果攻击者能够确定某一敏感功能的标识符,他就能像拥有高级权限的用户一样访问该功能。

多阶段功能

应用程序的许多功能通过几个阶段执行,并在执行过程中由客户端向服务器发送许多请求。例如,添加新用户功能可能包括从用户维护菜单中选取这个选项,从下拉列表中选择部门和用户职位,然后输入新用户名、初始密码和其他信息。

许多应用程序常常会努力防止这种敏感功能被未授权访问,但由于其误解了这种功能的使用方式,因而实施了不完善的访问控制。

在前面的示例中,如果一名用户试图加载用户维护菜单并从中选取“添加新用户”选项,应用程序就会核实该用户是否拥有必要的权限,如果用户未获授权,就阻止其进行访问。但是,如果攻击者直接进入核实用户所属部门和其他细节的阶段,可能就没有有效的访问控制对其加以限制。开发者认为,任何到达验证过程后续阶段的用户一定已经拥有相关的权限,因为前面的阶段已经验证了这些权限。通过这种方法,任何应用程序用户都能够添加一个新的管理用户账户,因而完全控制整个应用程序,访问许多其他已经实施完善的访问控制的功能。

即使在许多电子银行使用的安全性能很关键的Web应用程序中,我们也曾经发现这种类型的漏洞。在银行应用程序中,转账通常包括几个阶段,部分原因是为了防止用户在请求转账时无意出错。这个多阶段过程需要在每个阶段收集各种与用户有关的数据。这些数据在初次提交后将接受严格检查,然后使用HTML表单字段送交给随后的阶段。但是,如果应用程序并不在最后阶段重新确认这些数据,攻击者就可能会避开服务器检查。例如,应用程序可能会核实进行转账的来源账户是否属于当前用户,然后询问与目的账户有关的细节和转账的金额。如果用户拦截这个过程中的最后一个POST请求,并修改来源账号,他就能实现水平权限提升,从其他用户的账户中转移资金。

静态文件

在绝大多数情况下,用户都是通过向在服务器上执行的动态页面发布请求来访问受保护的功能和资源。这时,每个动态页面负责执行适当的访问控制检查,并确认用户拥有执行相关操作所需的权限。

但是,有些时候,用户会直接向位于服务器Web根目录下的静态资源提出请求,要求访问这些受保护的资源。例如,一个在线出版商允许用户浏览他的书籍目录并购买电子书进行下载。支付费用后,应用程序就将用户指向以下下载URL:

因为这是一个完全静态的资源,所以它并不在服务器上运行,它的内容直接由Web服务器返回。因此,资源自身并不能执行任何检查以确认提出请求的用户拥有必要的权限。如果可以通过这种方式访问静态资源,那么这些资源很可能没有受到有效的访问控制机制的保护,任何知晓URL命名方案的人都可以利用这种缺陷访问任何所需的资源。在上面的示例中,文档名称似乎是一个ISBN,利用这个信息,攻击者能够任意下载由该出版商制作的每一本电子书。

某些功能特别容易出现这种问题,包括提供公司年度报表之类静态文档的金融Web站点、提供可下载二进制代码的软件供应商以及通过其访问应用程序中静态日志文件和其他敏感数据的管理功能。

平台配置错误

一些应用程序在Web服务器或应用程序平台层使用控件来控制访问。通常,应用程序会根据用户的角色来限制对特定URL路径的访问。例如,如果用户不属于“管理员”组,访问/admin路径的请求可能会遭到拒绝。原则上,这是完全合法的访问控制方法。但是,如果在配置平台级控件时出现错误,这时就可能导致未授权访问。

正常情况下,平台级配置与防火墙策略规则类似,它们基于以下条件允许或拒绝访问请求:

HTTP请求方法;

URL路径;

用户角色。

如第3章所述,GET方法的最初目的是检索信息,而POST方法的目的是执行更改应用程序的数据或状态的操作。

如果没有小心制定规则,以基于正确的HTTP方法和URL路径允许访问,就可能会导致未授权访问。例如,如果用于创建新用户的管理功能使用POST方法,平台可能具有禁止POST方法并允许所有其他方法的拒绝规则。但是,如果应用程序级脚本并不验证针对此功能的所有请求是否使用POST方法,则攻击者就可以通过使用GET方法提交同一请求来避开这种控制。由于大多数用于检索请求参数的应用程序级API对于请求方法并无限制,因此,攻击者只需要在GET请求的URL查询字符串中提供所需参数,就可以未授权使用上述功能。

令人更加惊奇的是,即使平台级规则拒绝访问GET和POST方法,应用程序仍有可能易于受到攻击。这是因为,使用其他HTTP方法的请求可能最终由处理GET和POST请求的相同应用程序代码来处理。HEAD方法就是一个典型的例子。根据规范,服务器应使用它们用于响应对应的GET请求的相同消息头(但不包含消息主体)来响应HEAD请求。因此,大多数平台都能够正确处理HEAD请求,即执行对应的GET处理程序并返回生成的HTTP消息头。通常,GET请求可用于执行敏感操作,这或者是因为应用程序本身将GET请求用于这一目的(与规范不符),或者是因为它并不验证是否使用了POST方法。如果攻击者能够使用HEAD请求增加一个管理用户账户,那么,即使在请求中未收到任何消息主体,他仍然能够成功实施攻击。

某些情况下,对于使用无法识别的HTTP方法的请求,平台会直接将它们交由GET请求处理程序处理。在这种情况下,通过在请求中指定任意无效的HTTP方法,就可以避开拒绝某些指定的HTTP方法的平台级控制。

我们将在第18章中讨论Web应用程序平台产品中包含此类漏洞的一个特例。

访问控制方法不安全

一些应用程序使用一种极其不安全的访问控制模型,基于客户端提交的请求参数或受攻击者控制的其他条件做出访问控制决定。

基于参数的访问控制

在一些这种模型中,应用程序在用户登录时决定用户的角色或访问级别,并在登录后通过隐藏表单字段、cookie或者预先设定的查询字符串参数(参阅第5章了解相关内容)由客户端传送这些信息。应用程序在处理随后请求的过程中读取这个请求参数,并为用户分配相应的访问级别。

例如,使用应用程序的管理员将看到以下URL:

但普通用户看到的URL中包含一个不同的参数,或者根本不包含任何参数。任何知道分配给管理员的参数的用户只需在他自己的请求中使用这个参数,就可以访问管理功能。

有时候,如果不以高级权限用户的身份实际使用应用程序,并确定在使用过程中提出了哪些请求,这种类型的访问控制可能很难探测出来。作为普通用户,我们可以使用在第 4章讨论的如何发现隐藏请求参数的技巧成功查明这种机制。

基于Referer的访问控制

在其他不安全的访问控制模型中,应用程序使用HTTP Referer消息头做出访问控制决定。例如,应用程序可能会根据用户的权限,严格控制用户访问主维护菜单。但是,如果某名用户提出请求,要求访问某项管理功能,应用程序可能只是检查该请求是否由管理菜单页面提出,如果确实由该页面提出,即认为该用户一定已经访问过那个页面,并因此已经拥有了必要的权限。当然,从本质上讲,这种模型并不安全,因为Referer消息头完全由用户控制,并可设定为任何值。

基于位置的访问控制

许多公司都具有管理或业务要求,根据用户的地理位置限制对资源的访问。这些公司不仅包括金融机构,还包括新闻服务及其他部门。在这些情况下,公司会采用各种方法来确定用户的位置,其中最常用的是用户当前IP地址的地理位置。

攻击者能够轻易突破基于位置的访问控制。以下是一些常用的方法:

使用位于所需位置的Web代理服务器;

使用在所需位置终止的VPN;

使用支持数据漫游的移动设备;

直接修改客户端用于确定地理位置的机制。

攻击访问控制

在开始探查应用程序、检测任何实际的访问控制漏洞之前,渗透测试员应该花一些时间检查解析应用程序过程中得到的结果(请参阅第4章了解相关内容),了解应用程序在访问控制方面的实际要求,从而决定探查哪些内容可以得到最令人满意的结果。

渗透测试步骤

在分析应用程序的访问控制机制时,需要考虑以下问题。

(1)应用程序的功能是否允许用户访问属于他们的特定数据?

(2)是否存在各种级别的用户,如经理、主管、贵宾等,是否允许他们访问不同的功能?

(3)管理员是否使用内置在相同应用程序中、以对其进行配置和监控的功能?

(4)发现应用程序的哪些功能或数据资源最有可能帮助攻击者提升当前的权限?

(5)是否存在任何标识符(以POST消息体的URL参数的方式)表明正使用某一参数追踪访问控制级别?

使用不同用户账户进行测试

测试应用程序的访问控制效率的最简单、最有效的方法,是使用其他账户访问应用程序。这样你就可以确定,可由一个账户合法访问的资源和功能是否能够由另一个账户非法访问。

渗透测试步骤

(1)如果应用程序隔离用户对不同级别的功能的访问,可以首先使用一个权限较高的账户确定所有可用的功能,然后使用权限较低的账户访问这些功能,测试能否垂直提升权限。

(2)如果应用程序隔离用户对不同资源(如文档)的访问,可以使用两个不同的用户级账户测试访问控制是否有效,或者是否可以水平提升权限。例如,找到一个一名用户可以合法访问,但另一名用户不能合法访问的文档,然后尝试使用第二名用户的账户访问该文档——通过请求相关URL或在第二名用户的会话中提交同样的POST参数。

对应用程序的访问控制进行彻底测试需要耗费大量时间。幸运的是,一些工具可以帮助你自动完成某些工作,以提高测试速度和可靠性。这样,就可以将主要精力放在那些需要人类智能才能高效执行的任务上。

借助Burp Suite,可以使用两个不同的用户账户来解析应用程序的内容。然后,可以比较每一名用户访问的内容到底存在哪些差异。

渗透测试步骤

(1)将Burp配置为代理服务器并禁用拦截,以一个用户账户浏览应用程序的所有内容。如果要测试垂直访问控制,则使用权限较高的账户。

(2)检查Burp的站点地图的内容,确保已确定要测试的所有功能。然后使用上下文菜单选择“比较站点地图”(compare site maps)功能。

(3)要选择第二个进行比较的站点地图,可以从Burp状态文件中加载该地图,或让Burp在新会话中动态重新请求第一个站点地图。要测试同一类型的用户之间的水平访问控制,只需加载以前保存的、已将应用程序映射为其他用户的状态文件。要测试垂直访问控制,最好是以低权限用户身份重新请求高权限站点地图,因为这样可确保完全涵盖相关的功能。

(4)要在不同的会话中重新请求第一个站点地图,需要使用低权限用户会话的详细资料配置Burp的会话处理功能(例如,通过记录一个登录宏或提供要在请求中使用的特定cookie)。我们将在第14章中详细讨论此功能。还可能需要定义适当的范围规则,以防止Burp请求任何注销功能。

图8-1显示了一次简单站点地图比较的结果。其中的深色部分是站点地图之间差异分析的结果,这些部分显示了两个地图之间已添加、删除或修改的项目。对于已修改的项目,该表格提供了一个“diff count”列,其中列出了将第一个地图中的项目修改为第二个地图中的项目所需的编辑次数。而且,如果选中一个项目,其响应也以深色显示,以显示那些编辑在响应中的位置。

图8-1 显示以不同用户账户访问的内容之间的差异的站点地图比较

解释站点地图比较的结果需要一定的智慧,并需要了解特定应用程序功能的意义及用法。例如,图8-1显示了在用户查看主页时返回给每名用户的响应。其中的两个响应显示了针对登录用户的不同说明,而且管理用户拥有一个额外的菜单项。这些差异是预期行为,它们与应用程序访问控制的效率无关,因为它们只与用户界面有关。

用户请求顶级管理页面时返回的响应如图8-2所示。其中显示,管理用户可以看到一个包含可用选项的菜单,而普通用户则看到“未授权”(not authorized)消息。这些差异表明,访问控制已得到正确应用。用户请求“列举用户”(list users)管理功能时返回的响应如图8-3所示。其中显示的两个响应完全相同,这表示应用程序易于受到攻击,因为普通用户不应拥有访问此功能的权限,而且该用户的用户界面中也没有任何指向该功能的链接。

图8-2 低权限用户被禁止访问顶级管理页面

图8-3 低权限用户可以访问用于列举应用程序用户的管理功能

仅仅通过分析站点地图树及查看项目之间的差异数量,并不足以评估应用程序访问控制的效率。出现两个完全相同的响应可能表示存在漏洞(例如,在披露敏感信息的管理功能中),也可能不会导致任何危险(例如,在不受保护的搜索功能中)。相反,两个不同的响应也有可能表示存在漏洞(例如,在每次访问都返回不同内容的管理功能中),也可能不会导致任何危险(例如,在显示当前登录用户的用户信息的页面中)。基于上述原因,在确定访问控制漏洞方面,完全自动化的工具往往效率低下。使用Burp的“站点地图比较”功能,可以尽可能自动完成确定漏洞的过程,以现成的格式获得所需的全部信息,同时应用自己在应用程序功能方面的知识来确定任何具体的漏洞。

尝试访问

http://mdsec.net/auth/462/

http://mdsec.net/auth/468/

测试多阶段过程

上一节介绍的方法——比较通过不同用户账户访问的应用程序内容——无法用于测试某些多阶段过程。在多阶段过程中,要执行某个操作,用户通常需要以正确的顺序提出多个请求,应用程序则在用户提出请求的同时创建有关用户操作的状态。仅仅重新请求站点地图中的每一个项目,并不能正确重复相关过程,因此,由于访问控制以外的其他原因,你尝试的操作可能会失败。

以添加新应用程序用户的管理功能为例。该功能可能涉及几个步骤,包括加载用于添加用户的表单、提交包含新用户详细资料的表单、审查用户详细资料,以及确认添加操作。某些情况下,应用程序可能为会初始表单提供保护,但没有为处理表单提交的页面或确认页面提供保护。整个过程可能包含大量请求(包括重定向),在以前阶段提交的参数将在以后通过客户端重新传送。因此,这个过程的每一个步骤都需要单独进行测试,以确认访问控制是否得到正确应用。

尝试访问

http://mdsec.net/auth/471/

渗透测试步骤

(1)在以多步骤方式执行某个操作,需要从客户端向服务器提交几个不同的请求时,应单独测试每一个请求,以确定是否已对这些请求应用了访问控制。应确保测试每一个请求,包括表单提交、重定向,以及任何非参数化的请求。

(2)尝试发现应用程序确定你是否到达特定阶段(必须通过合法的途径到达该阶段)的任何位置。尝试使用权限较低的账户到达该阶段,检测是否可以实施任何权限提升攻击。

(3)手动执行这种测试的一种方法,是在浏览器中多次完成受保护的多阶段过程,并使用代理服务器将在不同请求中提供的会话令牌切换为权限较低的用户的令牌。

(4)通过使用Burp Suite的“浏览器中的请求”(request in browser)功能,可以显著加快这个过程。

(a)使用权限较高的账户遍历整个多阶段过程。

(b)使用权限较低的账户(或根本不使用账户)登录应用程序。

(c)在Burp Proxy的历史记录中,找到权限较高的用户执行多步骤过程时提出的请求序列。对于序列中的每个请求,选择“当前浏览器会话在浏览器中的请求”(request in browser in current browser session)上下文菜单项(如图8-4所示)。将提供的URL粘贴到以权限较低的用户身份登录的浏览器中。

图8-4 使用Burp在当前浏览器会话中请求给定项

(d)如果应用程序允许,则使用浏览器以正常方式完成剩下的多阶段过程。

(e)查看浏览器和代理服务器历史记录中的结果,确定是否可以成功执行特权操作。

当你对指定的请求选择Burp的“当前浏览器会话在浏览器中的请求”功能时,Burp会向你提供一个指定Burp的内部Web服务器的唯一URL,然后,将这个URL粘贴到浏览器的地址栏中。当你在浏览器中请求这个URL时,Burp将返回一个指向最初指定的URL的重定向。浏览器访问该重定向时,Burp将用最初指定的请求替换该请求,同时保持Cookie消息头不变。如果正测试不同的用户账户,可以加快这个过程。以不同用户登录几个不同的浏览器,并将上述URL粘贴到每个浏览器中,看应用程序如何处理使用不同浏览器登录的用户的请求。(需要注意的是,由于同一浏览器通常会在不同窗口之间共享cookie,因此,在执行这个测试时,需要使用不同的浏览器产品,或安装在不同机器上的浏览器。)

 提示 以不同的用户账户测试多阶段过程时,检查不同用户逐个提出的请求的序列有助于确定有利于进一步调查的细微差异。

如果使用不同浏览器以不同用户身份访问应用程序,可以在Burp中创建供每个浏览器使用的不同代理监听器(需要在每个浏览器中更新代理服务器配置,以指向相关监听器)。然后,对于每个浏览器,使用代理服务器历史记录中的上下文菜单打开新的历史记录窗口,并将显示过滤器设置为仅显示相关代理监听器提出的请求。

通过有限访问权限进行测试

如果只有一个用户级账户可用于访问应用程序(或根本没有任何账户),这时,要测试访问控制的效率,还需要完成其他工作。实际上,无论在什么情况下,要想执行全面彻底的测试,都需要完成其他工作。在应用程序中,可能存在一些未受到严格保护的功能,而且任何用户界面均未明确提供这些功能的链接。例如,可能有一些旧功能尚未删除,或者新功能已部署但未向用户公布。

渗透测试步骤

(1)使用第4章介绍的内容查找技巧确定尽可能多的应用程序功能。通常,以权限较低的用户进行查找就足以枚举并直接访问敏感功能。

(2)如果确定可能向普通和管理用户提供不同功能或链接的应用程序页面(例如,“控制面板”或“我的主页”),尝试在URL查询字符串和POST请求主体中插入admin=true之类的参数,确定这样做是否可发现或访问任何其他你所拥有的用户权限正常无法访问的功能。

(3)测试应用程序是否基于Referer消息头做出访问控制决策。对于获得授权访问的关键应用程序功能,尝试删除或修改Referer消息头并确定是否仍然能够成功提出请求。如果不能,应用程序可能以某种不安全的方式信任Referer消息头。如果使用Burp的主动扫描器扫描请求,Burp会尝试删除每个请求的Referer消息头,并通知你这样做是否会在应用程序的响应中造成对应的相关差异。

(4)检查所有客户端HTML与脚本,查找隐藏功能或可从客户端进行操纵的功能的引用,如基于脚本的用户界面。同时,反编译第5章中介绍的所有浏览器扩展组件,查找任何服务器端功能的引用。

尝试访问

http://mdsec.net/auth/477/

http://mdsec.net/auth/472/

http://mdsec.net/auth/466/

一旦枚举出所有可访问的功能,就有必要测试应用程序是否正确划分每名用户访问资源的权限。如果应用程序允许用户访问一组内容广泛的相同类型的资源(如文档、订单、电子邮件和个人资料),则用户就有机会未授权访问其他资源。

渗透测试步骤

(1)无论应用程序使用何种标识符(文档ID、账号、订单引用等)指定用户所请求的资源,应尝试找到没有权限访问的资源的标识符。

(2)如果有可能生成一系列紧密相连的标识符(例如,通过创建几个新文档或订单),则可以使用我们在第7章描述的针对会话令牌的技巧,尝试在应用程序生成的标识符中查找任何可预测的序列。

(3)如果无法生成任何新标识符,那么只能通过分析已经发现的标识符,或纯粹使用猜测方法查找标识符。如果标识符为GUID形式,则基于猜测的尝试将无法取得成功。但是,如果标识符是一个相对较小的数字,则可以尝试使用与它相差不大的另一个数字,或数字位相同的另一个随机数字。

(4)如果发现访问控制并不完善,而且资源标识符可以预测,可以发动自动攻击获取应用程序的敏感资源和信息。可以使用在第14章描述的技巧,设计一次定制自动攻击,以获取所需的数据。

如果“账户信息”页面在显示用户个人资料的同时还显示他的用户名和密码,则这种漏洞可能会造成灾难性的后果。虽然输入的密码在屏幕上隐藏显示,但它仍然以明文形式传送至浏览器。因此,通常可以快速遍历账户标识符的所有可能值范围,从而获得所有用户,包括管理员的登录证书。图8-5说明了如何使用Burp Intruder成功执行这种攻击。

图8-5 一次通过访问控制漏洞获取用户名和密码的成功攻击

尝试访问

http://mdsec.net/auth/488/

http://mdsec.net/auth/494/

 提示 如果检测到访问控制漏洞,可以立即发动一次攻击,尝试通过攻破一个具有管理权限的用户账户来进一步提升自己的权限。可以通过各种技巧查找管理账户。利用上面介绍的访问控制缺陷,可以获得数百个用户证书,并尝试手动登录每一个账户,直到找到管理员账户。但是,如果账户以连续的数字ID为标识符,则应用程序通常会将最小的数字账户分配给管理员。登录几个最先注册的用户,往往就可以确定应用程序管理员账户。如果这种方法不可行,一种有效的方法是在应用程序中查找其访问权限被水平隔离的功能——例如,向每名用户呈现的主页。编写一段脚本,使用截获的每个证书登录,然后尝试访问自己的主页。很可能管理用户能够查看每一名用户的主页,因此,如果用于登录的是管理账户,你立即就会发觉。

测试“直接访问方法”

如果应用程序使用直接访问服务器端 API 方法的请求,正常情况下,使用上述技巧即可以确定这些方法中的任何访问控制漏洞。但是,还应该进行测试,以确定是否存在其他可能未受到正确保护的 API。

以使用下列请求调用的servlet为例:

由于这是一个众所周知的servlet,攻击者可能能够访问其他servlet以执行未授权操作。

渗透测试步骤

(1)确定任何遵循Java命名约定(例如get、set、add、update、is或has后接大写单词)或明确指定包结构(如com.companyname.xxx.yyy.ClassName)的参数。记下所有你能够发现的被引用的方法。

(2)找到某个列举可用接口或方法的方法。在代理服务器历史记录中进行搜索,看应用程序的正常通信是否调用了该方法。如果该方法未被调用,则尝试使用观察到的命名约定猜测该方法。

(3)在公共资源(如搜索引擎和论坛站点)中查找,以确定任何其他可以访问的方法。

(4)使用第4章介绍的技巧猜测其他方法名称。

(5)尝试使用各种用户账户(包括未授权访问)访问收集到的所有方法。

(6)如果不知道某些方法需要的参数的数量或类型,可以寻找那些不大可能使用参数的方法,如listInterfaces和getAllUsersInRoles。

测试对静态资源的控制

如果受应用程序保护的静态资源最终可以通过指向资源文件本身的URL直接访问,这时你应该进行测试,以确定未授权用户是否可以直接请求这些URL。

渗透测试步骤

(1)遍历访问受保护静态资源的正常过程,获取用于最终访问该资源的URL示例。

(2)使用不同的用户账户(如权限较低的用户或没有购买所需商品的账户),尝试使用已确定的URL直接访问该资源。

(3)如果这种攻击取得成功,尝试了解受保护的静态资源所使用的命名方案。如果可能,设计一个自动攻击,获取可能有用或可能包含敏感数据的内容(请参阅第14章)。

测试对HTTP方法实施的限制

虽然并没有现成的方法可用于检测应用程序的访问控制是否对HTTP方法实施了平台级控制,但是,可以通过一些简单的步骤来确定任何漏洞。

渗透测试步骤

(1)使用一个权限较高的账户,确定一些执行敏感操作的特权请求,如添加新用户或更改用户的安全角色的请求。

(2)如果这些请求未受到任何反CSRF令牌或类似功能(请参阅第13章)的保护,可以使用权限较高的账户确定,如果HTTP方法被修改,应用程序是否仍然执行请求的操作。应测试的HTTP方法包括:

POST;

GET;

HEAD;

任何无效的HTTP方法。

(3)如果应用程序执行任何使用与最初的方法不同的HTTP方法的请求,则应使用上述标准技巧,通过权限较低的账户对针对这些请求实施的访问控制进行测试。

保障访问控制的安全

访问控制是最容易理解的Web应用程序安全领域,但是在执行它们时必须采用合理、全面的方法。

首先应避免几种明显的缺陷。出现这些缺陷,通常是由于我们不了解执行有效访问控制应满足的基本要求,或者对用户应提出哪些请求,以及应用程序需要防御哪些威胁存在错误的认识。

不要认为用户不知道用于指定应用程序资源的URL或标识符(如账号和文档ID)就无法访问这些资源。假设用户知道每一个应用程序的URL和标识符,确保应用程序的访问控制足以防止未授权访问。

不要信任任何用户提交的表示访问权限的参数(如admin=true)。

不要认为用户将按设定的顺序访问应用程序页面。不要认为因为用户无法访问“编辑用户”页面,他们就不能到达由该页面链接的“编辑用户X”页面。

不要相信用户不会篡改通过客户端传送的数据。如果用户提交的一些数据已被确认,然后通过客户端传送,不要不经重新确认就相信传送的值。

以下是一些在Web应用程序中执行有效访问控制的最佳方法。

仔细评估并记录每个应用程序功能单元的访问控制要求。这包括谁能合法使用这些功能,以及用户通过这些功能能够访问哪些资源。

通过用户会话做出所有访问控制决定。

使用一个中央应用程序组件检查访问控制。

通过这个组件处理每一个客户端请求,确认允许提出请求的用户访问他请求的功能和资源。

使用编程技巧确保前面的方法没有例外。一种有效的方法是规定每个应用程序页面必须采用一个由中央访问控制机制查询的界面。强制开发者将访问控制逻辑代码写入每个页面,不得找借口省略这些代码。

对于特别敏感的功能,例如管理页面,可以通过IP地址进一步限制访问,确保只有特殊网络范围内的用户能够访问这些功能,不管他们是否登录。

如果静态内容需要得到保护,有两种方法可提供访问控制。首先,用户可通过向执行相关访问控制逻辑的服务器端动态页面传送一个文件名,间接访问静态文件。其次,可通过使用HTTP验证或应用程序服务器的其他特性隐藏进入的请求,并在允许访问前检查资源许可,控制用户直接访问静态文件。

无论何时通过客户端传送,指定用户所希望访问资源的标识符都容易遭到篡改。服务器应只信任完整的服务器端数据。任何时候通过客户端传送这些标识符,都需要对它们进行重新确认,以确保用户拥有访问被请求资源的授权。

对于安全性很关键的应用程序功能(如在银行应用程序中创建一个新的汇款收款人)考虑对每笔交易执行重复验证和双重授权,进一步确保该功能不会被未授权方使用。这样做还可以减轻其他可能的攻击(如会话劫持)造成的后果。

记录每一个访问敏感数据或执行敏感操作的事件。这些记录有助于检测并调查潜在的访问控制违反事件。

Web应用程序开发者通常逐步执行访问控制功能,在他们发现需要访问控制的每个页面插入代码,并在不同的页面间剪切和粘贴相同的代码以满足类似的需求。这种方法会在建立的访问控制机制中引入内在的缺陷:许多需要访问控制的情况被忽略;为一个区域设计的控制可能并不适用于另一个区域;在应用程序其他地方所做的修改可能会由于违反开发者做出的假设而与现有控制机制相互冲突。

与这种方法相比,前面描述的使用中央应用程序组件实施访问控制的方法具有诸多优点。

它可增进应用程序访问控制的透明度,使得不同开发者能够迅速理解其他人执行的控制机制。

它可提高访问控制的可维护性。许多变更只需要在一个共享的组件中应用一次即可,不需要将代码剪切并粘贴到多个位置。

它可改善可适应性。如果出现新的访问控制要求,这些要求可立即反映到由每个应用程序页面执行的一个现有API中。

它比在整个应用程序中逐步执行访问控制代码造成的错误和遗漏更少。

多层权限模型

与访问有关的问题不仅适用于Web应用程序,而且也适用于其中的其他基础设施,特别是应用程序服务器、数据库和操作系统。采取深层安全措施需要在上述每一个层面执行访问控制,建立几层保护。这样做可以强化对未授权访问威胁的防御,因为即使攻击者攻破一个层面的防御,也会被其他层面的防御机制阻止。

除上文所述的在Web应用程序中执行有效的访问控制外,还可以通过各种方式将这种多层次的方法应用于应用程序的基础组件中,举例如下。

可根据在应用程序服务器层面定义的用户角色,使用应用程序服务器对完整URL路径实施访问控制。

当执行其他用户的操作时,应用程序可使用一个不同的数据库账户。应为仅需查询(而非更新)数据的用户提供一个只读权限账户。

应使用一个权限表,对数据库中不同的数据库表执行严格的访问控制。

用于运行基础设施中每个组件的操作系统账户只需分配组件实际需要的最低权限。

复杂的安全性能关键的应用程序可通过一个定义应用程序不同用户角色和不同权限的矩阵来帮助实施这种多层防御措施。在每一个层面,应将不同的权限分配给每一个角色。图8-6是一个复杂应用程序的一部分权限矩阵。

图8-6 一个复杂应用程序权限矩阵实例

我们可以在这种安全模型中应用各种有益的访问控制概念。

编程控制(programmatic control)。数据库权限矩阵保存在一个数据库表中,并以编程的形式来做出访问控制决定。对用户角色进行分类可以简化某些访问控制检查,这一任务同样可以通过编程来完成。编程控制可能极其琐碎,并可能在应用程序中建立非常复杂的访问控制逻辑。

自主访问控制(Discretionary Access Control,DAC)。使用自主访问控制,管理员可将自己的权限分配给其他与拥有特殊资源有关的用户。在封闭型DAC模型中,除非明确许可,否则拒绝访问。管理员还可以锁定或终止某个用户账户。在开放型DAC模型中,除非明确废除许可,否则允许访问。各种应用程序用户有权创建用户账户,并再次应用自主访问控制。

基于角色的访问控制(Role-Based Access Control,RBAC)。这种控制使用许多命名的角色,每个角色拥有各不相同的特殊权限;每个用户分配有一个这样的角色。这样做可简化不同权限的分配与实施,并有助于管理复杂应用程序中的访问控制。使用角色对用户请求执行“前沿”访问检查有助于实行最少量的处理迅速拒绝许多未授权的请求。对特殊用户可访问的URL路径加以保护就是这种方法的一个典型应用。

当设计基于角色的访问控制机制时,我们有必要限制角色的数量,以对应用程序的权限进行有效管理。如果建立太多琐碎的角色,那么由于不同角色的数目繁多,可能就很难对其进行有效管理。如果建立太少的角色,这些角色就只能对访问进行粗略管理,个体用户分配到的权限将不足以履行他们的职能。

如果使用平台级控制、基于HTTP方法和URL限制对不同应用程序角色的访问,则应将这些控制设计为使用默认拒绝模式,因为这是防火墙规则的最佳做法。这其中应包括各种特定规则,用于将某些HTTP方法和URL分配给特定角色,而且,随后的规则应拒绝不符合前一规则的任何请求。

声明式控制(declarative control)。应用程序使用有限的数据库账户访问数据库。它对不同的用户群体使用不同的账户,每个账户分配到执行该群体所允许执行的操作所必需的最低权限。这种声明式控制从应用程序以外进行声明。这是深层防御原理的一个非常有用的应用,因为权限是由另外一个组件赋予应用程序的。这样,即使一名用户突破在应用程序层面执行的访问控制,企图实施添加新用户之类的敏感操作,他仍然会被阻止,因为他使用的数据库账户并未在数据库内获得必要的权限。

另一种情况是在配置应用程序的过程中,通过配置描述符文件(descriptor file)在应用程序服务器层面上应用声明式访问控制。但是,这种应用一般相对简单,并且无法进行扩展,所以无法管理大型应用程序中种类繁多的权限。

渗透测试步骤

如果攻击一个采用这种多层权限模型的应用程序,可能这个应用程序能够防御在应用访问控制过程中常犯的许多明显错误。由于在其他层面实施的保护措施,避开应用程序的访问控制可能无法取得很大成效,但仍然可以使用其他可能的攻击手段。更重要的是,了解每种控制在它所能提供的保护方面存在的限制,将有助于确定最可能影响到它的漏洞。

应用程序层面的编程检查易于受到注入类攻击。

在应用程序服务器层面定义的角色,其定义既不全面,也不完整。

即使使用低权限操作系统账户运行应用程序组件,这些账户通常仍然能够阅读主机文件系统中保存的各种敏感数据。任何准许他人访问任意文件的漏洞(即使仅仅读取敏感数据)都可被攻击者加以有效利用。

应用程序服务器软件本身存在的漏洞往往有助于突破应用程序层面执行的任何访问控制,但是仍然只能有限地访问数据库和操作系统。

在适当位置的一个可供利用的访问控制漏洞可成为发动重大权限提升攻击的起点。例如,如果能够修改与账户有关的角色,那么再次使用账户登录将能够提升在应用程序和数据库层面的访问权限。

小结

访问控制缺陷可能以各种形式表现出来。有些时候,它们可能并没有利用价值,允许访问不能进一步提升权限的“无害”功能。其他情况下,利用在访问控制中发现的一个漏洞攻击者可立即攻破整个应用程序。

造成访问控制缺陷的来源各异:设计糟糕的应用程序很难或无法检测出未授权访问,一个简单的疏忽可能会使一两项功能未受到保护,或者对用户行为的错误假设也可能会给应用程序造成防御漏洞。

许多时候,突破访问控制非常容易,只需请求一个常用的管理URL就可以直接访问相关功能。但是,有时突破访问控制也可能非常困难,一些细微的缺陷可能在应用程序之中隐藏得较深,特别是在复杂、高度安全的应用程序中。“四处查看”是攻击访问控制的最有效方法。如果想努力取得进展,渗透测试员应该耐心测试应用程序的每一项功能,也许不久就可以发现一个能攻破整个应用程序的缺陷。

问题

欲知问题答案,请访问http://mdsec.net/wahh。

(1)一个应用程序可能通过使用HTTP Referer消息头实施访问控制,但它的正常行为并没有公开表露这一点。如何检测出这种缺陷?

(2)登录一个应用程序后,被重定向到以下URL:

https://wahh-app.com/MyAccount.php?uid=1241126841

应用程序似乎向MyAccount.php页面提交一个用户标识符。已知的唯一标识符是自己的标识符。如何测试应用程序是否使用这个参数以不安全的方式实施访问控制?

(3)因特网上的一个Web应用程序通过检查用户的来源IP地址实施访问控制。为什么这种行为可能存在缺陷?

(4)某应用程序的唯一目的是为公众提供可搜索的信息仓库。该应用程序并未使用任何验证或会话管理机制。该应用程序应执行何种访问控制?

(5)在浏览一个应用程序的过程中遇到几个应防止未授权访问的敏感资源,它们的文件扩展名为.xls。这种情况为何应立即引起注意?

浙ICP备11005866号-12