(1)如果已经知道一个有效的用户名(例如一个受控制的账户),使用这个用户名和一个错误的密码进行一次登录,然后使用一个完全随机的用户名进行另一次登录。
(2)记录服务器响应两次登录尝试的每一个细节,包括状态码、任何重定向、屏幕上显示的信息以及任何隐藏在HTML页面中的差异。使用拦截代理服务器保存服务器上来回流量的完整历史记录。
(3)努力找出服务器响应两次登录尝试的任何明显或细微的差异。
(4)如果无法发现任何差异,在应用程序中任何提交用户名的地方(例如自我注册、密码修改与忘记密码功能)重复上述操作。
(5)如果发现服务器响应有效和无效用户名之间的差异,收集一个常见用户名列表并使用一个定制脚本或自动工具迅速提交每个用户名,过滤出说明用户名有效的响应(请参阅第14章了解相关内容)。
(6)开始枚举操作之前,请确定应用程序是否在登录尝试失败次数达到一定数目后执行账户锁定(请参阅6.2.2节)。如果应用程序执行账户锁定,最好在设计枚举攻击时记住这一点。例如,如果应用程序只允许登录某个账户时失败3次,可能就会在使用通过自动枚举发现的每 个用户名登录时“浪费” 一次登录机会。因此,当进行枚举攻击时,不要在每次登录时提交完全不合理的密码,而是提交常见的密码,如password1或以用户名为密码。如果应用程序执行脆弱的密码强度规则,在枚举操作过程中执行的一些登录尝试就很可能会取得成功,有些情况下还可能同时查明用户名和密码。要以用户名设置密码字段,可以使用Burp Intrude冲的“破城槌”(battering ram)攻击模式,在登录请求的几个位置插入相同的有效载荷。
即使应用程序对包含有效与无效用户名登录尝试的响应完全相同,我们仍然可以根据应用程序响应登录请求的时间枚举出用户名。应用程序通常依据登录请求是否包含有效用户名,对其进行截然不同的后端处理。例如,如果登录请求中包含一个有效的用户名,应用程序可能会从后端数据库中获取用户资料,对这些资料进行各种处理(如检查账户是否到期),然后确认密码(可能使用一个资源密集型散列算法),如果密码错误返回一条常规消息。仅仅使用浏览器可能无法检测出应用程序处理两个请求之间的时间差异,但自动工具能够区分这种差异。即使这种操作会产生大量错误警报,但100个用户名约50%的有效率仍然要强于10 000个用户名仅0.5%的有效率。第15章将详细讨论如何检测并利用这种时间差异从应用程序中提取信息。
提示
除登录功能外,我们还可以从其他地方获取有效的用户名。检查在应用程序解析过程中(请参阅第4章了解相关内容)发现的所有源代码注释,确定所有明显的用户名。开发者或组织内部内其他人员的电子邮件地址都可能为有效的用户名;任何可访问的日志功能也可能透露用户名。