这些缺陷可严重危及应用程序的安全,但是,如果攻击者仅拦截浏览器请求并修改被提交的参数值,还是无法确定其中任何一个缺陷。
(1)只要应用程序通过几个阶段执行一项关键操作,就应该提取在某个阶段提交的参数,然后尝试在另一个阶段提交这些参数。如果相关数据随应用程序的状态一起更新,应该探索这 种行为的衍生效果,确定是否可以利用它实施任何恶意操作,如前面的3个示例所述。
(2)如果应用程序执行一项功能,不同类型的用户可根据一组共同的数据更新或执行其他操作,应该利用每种类型的用户执行该功能并观察他们提交的参数。如果不同的用户提交不同的参数,就提取由一名用户提交的每个参数,并尝试以其他用户的身份提交这些参数。如果应用程序接受并处理这些参数,如前面所述,探索这种行为的衍生效果。
我们曾在一家大型金融服务公司使用的Web应用程序中遇到过这种逻辑缺陷。
1.功能
应用程序允许尚未使用在线应用程序的顾客进行注册。然后,应用程序要求新用户提供一些基本的个人信息,在一定程度上确认他们的身份。这些信息包含姓名、地址和出生日期,但并不包括任何机密信息,如现有密码或 PIN 号码。
顾客正确输入这些信息后,应用程序再将注册请求转交给后端系统处理。然后,再向用户注册的家庭地址邮寄一个信息包裹。包裹内含有如何通过给公司呼叫中心拨打电话激活在线访问的指导,以及用户在第一次登录应用程序时使用的一次性密码。
2.假设
应用程序的设计者认为这种机制可为防止未授权访问提供强大的保护。该机制实施以下3层保护。
应用程序要求用户提前输入一部分个人信息,阻止恶意攻击者或恶作剧用户以其他用户的身份进行注册。
注册过程包括以非常规邮寄的形式向顾客注册的家庭地址传送一些机密信息。要想实施攻击,任何攻击者都必须盗取受害人的个人邮件。
注册功能要求顾客给呼叫中心拨打电话,并根据个人信息与在PIN号码中选择的数字,以常规方式核实他们的身份。
这种设计确实非常安全。但是,该机制的实际执行过程存在逻辑缺陷。
执行注册机制的开发者需要以某种方式保存用户提交的个人信息,并将它们与公司数据库中储存的客户身份关联起来。由于希望重复利用现有代码,他们使用以下这个似乎能够满足要求的类:
获得用户信息后,这个对象被实例化,与提交的信息一起保存在用户会话中。然后,应用程序核对用户信息,如果信息有效,就给该用户分配一个唯一的顾客号码,并将其用在公司的所有系统中。随后,应用程序将这个号码连同用户的其他一些有用信息,一起添加到这个对象中。最后,这个对象被传送至处理注册请求的后端系统进行处理。
开发者认为使用这个代码组件并无妨碍,不会造成任何安全问题。然而,这种错误的假设可能会造成严重的后果。
3.攻击方法
应用程序的其他功能(包括核心功能)也使用合并到注册功能中的相同代码组件,核心功能允许通过验证的用户访问账户、账目、转账和其他信息。一名注册用户成功通过应用程序的验证后,这个对象也被实例化,并保存在他的会话中,用于存储与其身份有关的关键信息。应用程序的绝大多数功能在执行操作时引用这个对象中保存的信息。例如,应用程序根据保存在这个对象中的唯一顾客号码生成在用户主页面显示的账户信息。
应用程序的其他功能已经使用这个代码组件,意味着开发者的假设存在缺陷,应用程序重复使用它们的方式确实会造成一个巨大的漏洞。
虽然这个漏洞非常严重,但实际上我们很难发现并利用这个漏洞。应用程序的主要功能受到几层访问控制的保护,用户需要拥有一个完全合法的会话才能通过这些控制。因此,为利用这个逻辑缺陷,攻击者需要执行以下步骤。
使用他自己的有效账户证书登录应用程序。
使用登录后得到的通过验证的会话,访问注册功能并提交另一名顾客的个人信息。这样,应用程序就会用一个与目标顾客有关的对象,重写攻击者会话中最初的CCustomer对象。
返回应用程序主要功能并访问其他顾客的账户。
从“黑盒”角度探查应用程序时,这种漏洞并不明显。同时,当审查或编写源代码时,我们也很难发现它。如果未能明确、全面地了解应用程序及其在不同区域使用的各种组件,我们可能无法知道开发者做出的错误假设。当然,添加明确注释的源代码与设计文档也有助于降低引入或探测不到这种缺陷的可能性。