发现并利用这种缺陷的技巧叫作强制浏览,包括避开浏览器导航对应用程序功能访问顺序实施的任何控制。
(1)如果一个多阶段过程需要按预定的顺序提交一系列请求,尝试按其他顺序提交这些请求。尝试完全省略某些阶段、几次访问同一个阶段或者推后访问前一个阶段。
(2)这些阶段的结果可通过一系列指向特殊URL的GET或POST请求进行访问,或者需要向同一个URL提交不同的参数。被访问的阶段可通过在被请求的参数中提交功能名称或索引来指定。确保完全了解应用程序访问特殊阶段所使用的机制。
(3)根据执行功能的情形,试图了解开发者做出的假设及主要受攻击面位于何处。设法找到违反这些假设从而在应用程序中造成反常行为的方法。
(4)如果不按顺序访问多阶段功能,应用程序常常表现出一系列异常现象,如变量值为空字符或未被初始化、状态仅部分定义或相互矛盾以及其他无法预料的行为。这时,应用程序可能会返回有用的错误消息与调试结果,可用于充分了解其内部机制并对当前或其他攻击进行优化(请参阅第15章了解相关内容)。有时,应用程序可能会进入一种完全出乎开发者意料的状态,导致严重的安全缺陷。
注解
许多类型的访问控制漏洞与这种逻辑缺陷类似。如果一项特权功能需要完成几个按预定顺序访问的阶段才能实现处理,应用程序可能认为用户总会按这个顺序处理该项功能。应用程序可能会对这个过程的初始阶段实施严格的访问控制,并认为任何到达后面阶段的用户一定已经获得相关授权。如果一个低权限的用户直接进入了后面的一个阶段,他就能够无限制地访问这个功能。请参阅第8章了解查找并利用这种漏洞的详情。
我们曾在一家金融服务公司使用的Web应用程序中遇到过这种逻辑缺陷。
1.功能
应用程序为用户提供保险报价,如果需要,用户可在线完成并提交一份保险申请。这个过程包括如下几个阶段。
第一阶段,申请人提交一些基本信息,并指定首选月保费或希望投保的金额。应用程序提供一个报价,同时计算申请人并未指定的其他值。
后几个阶段,申请人提交其他各种个人信息,包括健康状况、职业与爱好。
最后,应用程序连接一名为保险公司工作的保险员。保险员使用该Web应用程序审核申请人提交的信息,并决定是否接受申请,或者修改最初的报价以反映任何额外的风险。
在上述的每一个阶段中,应用程序使用一个共享组件处理用户提交的每一个参数。这个组件将每个POST请求中的所有数据解析成名/值对,并使用收到的数据更新其状态信息。
2.假设
处理用户提交的数据的组件认为每个请求仅包含用户在相关HTML表单中提交的参数。而开发者并未考虑到这种情形:如果一名用户提交了应用程序并不希望他提交的参数,将会出现什么情况。
3.攻击方法
上述假设当然存在缺陷,因为用户可在每个请求中提交任意参数与参数值。因此,应用程序的核心功能有许多不完善的地方。
攻击者可以利用共享组件避开所有服务器端的输入确认。在报价过程的每一个阶段,应用程序对这些阶段提交的数据执行严格的确认,并拒绝任何未通过这种确认的数据。但是,共享组件使用用户提交的每一个参数更新应用程序的状态。因此,如果攻击者提供应用程序在较早阶段需要的一个名/值对,不按顺序提交数据,那么应用程序将不对其进行任何确认,直接接受并处理该数据。如果出现这种情况,恶意用户就可以据此实施针对保险员的保存型跨站点脚本攻击,访问属于其他应用程序的个人信息(请参阅第12章了解相关内容)。
攻击者能够以任意价格购买保险。在报价过程的第一阶段,申请人指定他们首选的月保费或希望投保的金额,应用程序据此计算其他值。然而,如果用户在后续某个阶段为上面的一个或几个数据项提交新的值,那么应用程序将根据这些值更新自己的状态。不按顺序提交这些参数,攻击者就可以获得任意价格的保险报价及任意月保费。
应用程序并不对某一类用户能够提交哪些参数实施访问控制。当保险员审核完成的申请时,他们会更新各种数据,包括做出承保决定。这些数据由处理普通用户提交的数据的同一个共享组件处理。如果攻击者知道或猜测出保险员在审查申请时使用的参数名称,就可以提交这些参数,不用签署保单即可接受自己的申请。