一、重置凭证泄漏
在逻辑漏洞中,任意用户密码重置最为常见,可能出现在新用户注册页面,也可能是用户登录后重置密码的页面,或者用户忘记密码时的密码找回页面。其中,密码找回功能是重灾区。我把日常渗透过程中遇到的案例作了漏洞成因分析,这次,关注因重置凭证泄漏导致的任意用户密码重置问题。
案例一
用邮件找回密码时,作为重置凭证的验证码在 HTTP 应答中下发客户端,抓包后可轻易获取。先用攻击者账号走一次密码找回流程,测试账号 yangyangwithgnu@yeah.net 选用邮箱找回密码:
点击获取校验码后抓取如下应答:
其中,VFCode 从字面理解很可能是校验码。登录邮箱查看网站发过来的密码找回邮件:
发现两者一致,那么,几乎可以确认服务端将密码找回的校验码泄漏至客户端,可导致任意账号密码重置问题。
尝试找回普通账号的密码。密码找回首页输入邮箱后,系统将立即校验该邮箱是否注册:
将 UName 参数定义为枚举变量,以常见 qq 邮箱作为字典,可枚举出多个有效邮箱:
以 chenwei@qq.com 为例,在应答包中找到校验码,成功将其密码重置为 PenTest1024,验证可登录:
尝试找回管理员账号的密码。从该网站的域名注册信息中找到联系人的邮箱为 fishliu@xxxx.cn,可推测后台用户的邮箱后缀为 @xxxx.cn,所以,用常见后台用户名简单调整可构造出后台用户邮箱字典,枚举出大量后台用户:
同理可重置这些后台用户的账号密码,为避免影响业务,不再实际操作。
案例二
用邮件找回密码时,带凭证的重置链接泄漏至客户端,抓捕可获取。用攻击者账号走一次密码找回流程。在找回密码页面输入攻击者账号及其邮箱(yangyangwithgnu、yangyangwithgnu@yeah.net)后提交:
拦截如下应答:
显然是个重定向,isVerify、PassPhrase 这两个参数很可疑,后续交互中应留意,先放包,进入发送重置邮件的页面,输入验证码后提交。登录攻击者邮箱查看重置邮件:
这个带 token 的重置链接似曾相识,对,就是前面抓包获取的 token 信息,比对看下:
forgotPwdEa.php?isVerify=eWFuZ3lhbmd3aXRoZ251fHlhbmd5YW5nd2l0aGdudUB5ZWFoLm5ldHw2MzQyNDkw&PassPhrase=01e4f6d4ede81b2604dc320bc4e3a6e8
forgotPwdEc.php?isVerify=eWFuZ3lhbmd3aXRoZ251fHlhbmd5YW5nd2l0aGdudUB5ZWFoLm5ldHw2MzQyNDkw&PassPhrase=01e4f6d4ede81b2604dc320bc4e3a6e8
唯一区别 forgotPwdEa 和 forgotPwdEc 两个文件名。
接下来验证通过服务端泄漏的 token 能否重置普通用户的账号密码。从重置流程可知,要重置密码必须提供用户名及其邮箱(或手机号)。
获取有效用户名。注册页面中,输入用户名后立即校验该用户名是否被占用:
对应请求、应答如下:
用户名已存在返回 failed,不存在返回 ok。以此特征,用常见国人姓名字典,可枚举出大量有效用户名(如 chenchuan、chenanqi、chenanxiu、zhangfeng 等等),存为 username.txt。
获取有效用户名对应邮箱。密码找回首页提交的请求中,user_name 与 email 参数匹配情况下,HTTP 应答代码为 302,交互包如下:
可以此特征枚举有效用户名及其邮箱。现在考虑如何制作邮箱字典?很多用户喜欢用用户名注册 qq 邮箱,换言之,用户名 yangyangwithgnu 可能对应邮箱 yangyangwithgnu@qq.com。所以,用前面已经获取有效用户名字典 username.txt 快速制作了邮箱字典 qq-email.txt,其中,username.txt 与 qq-email.txt 逐行对应。
例如,前者第一行为 yangyangwithgnu、后者第一行为 yangyangwithgnu@qq.com。将上面的数据包放入 burp 的 intrduer 中,攻击类型选 pitchfork、user_name 的参数值定义为枚举变量 1 并加载字典 username.txt、email 的参数值定义为枚举变量 2 并加载字典 qq-email.txt,可枚举出大量有效用户名/邮箱信息,如,zhangfeng/zhangfeng@qq.com、chenchuan/chenchuan@qq.com 等等。
用普通账号 chenchuan/chenchuan@qq.com 演示密码重置漏洞。输入用户名、密码提交,正常完成密码找回逻辑,从交互包中获取服务端下发的重置 token:
isVerify=Y2hlbmNodWFufGNoZW5jaHVhbkBxcS5jb218MTE2MDIzNw==&PassPhrase=cbf0160662358808f3586868f041cbaa
拼装为重置链接 http://www.xxxx.com/user/forgotPwdEc.php?isVerify=Y2hlbmNodWFufGNoZW5jaHVhbkBxcS5jb218MTE2MDIzNw==&PassPhrase=cbf0160662358808f3586868f041cbaa ,访问之,即可进入密码重置页面:
输入新密码 PenTest1024 后系统提示修改成功。用 chenchuan/PenTest1024 成功登录:
防御措施上,密码找回的凭证切勿下发客户端,另外,校验邮箱是否有效应添加图片验证码,以防止关键参数被枚举。
二、重置凭证接收端可篡改
密码找回逻辑含有用户标识(用户名、用户 ID、cookie)、接收端(手机、邮箱)、凭证(验证码、token)、当前步骤等四个要素,若这几个要素没有完整关联,则可能导致任意密码重置漏洞。
案例一:接收端可篡改。请求包中包含接收端参数,可将凭证发至指定接收端。
密码重置页面,输入任意普通账号,选择手机方式找回密码。在身份验证页面点击获取短信验证码:
拦截请求,发现接收验证码的手机号为请求包中的参数:
直接篡改为攻击者的手机号,成功接收短信验证码,提交验证码后,正常执行 3、4 步即可成功重置该账号的密码。
案例二:接收端可篡改。请求包中出现接收端间接相关参数,可将凭证发至指定接收端。
在密码找回页面,用攻击账号 test0141,尝试重置目标账号 2803870097 的密码(对滴,你没看错,这两个长得完全不像的账号的确是同个网站的)。
在第一个首页中输入 test0141 和图片验证码完成“01 安全认证”:
请求为:
输入图片验证码获取短信验证码完成“02 身份验证”:
请求为:
后续的 03、04 步不涉及用户名信息,忽略。
全流程下来,客户端并未直接提交接收短信验证码的手机号,多次尝试可知,02 中出现的 user_name 用于查询下发短信的手机号,用它可以间接指定接收端,那么,它是否仅此作用而不用于指定重置密码的账号?如下思路验证,先将 userName 置为 2803870097 完成 01 以告诉服务端重置的账号,再将 user_name 置为 test0141 完成 02 以欺骗服务端将短信验证码发至攻击者手机,顺序完成 03、04 或许能实现重置 2803870097 的密码。具体如下。
第一步,用普通账号 2803870097 进行安全认证:
第二步,对普通账号 2803870097 进行身份验证:
拦截发送短信验证码的请求:
将 user_name 从 2803870097 篡改为 test0141,控制服务端将验证码发至 test0141 绑定的手机号:
test0141 的手机号成功接收到验证码 872502,将该验证码填入重置 2803870097 的身份校验页面后提交:
第三步,输入新密码 PenTest1024 后提交,系统提示重置成功:
第四步,用 2803870097/PenTest1024 登录,验证成功:
防御措施方面,一定要将重置用户与接收重置凭证的手机号/邮箱作一致性比较,通常直接从服务端直接生成手机号/邮箱,不从客户端获取。
三、用户混淆
这次,关注因用户混淆导致的任意用户密码重置问题。
案例一:通过 cookie 混淆不同账号,实现重置任意用户密码。
密码找回页面 https://my.xxxx.com/pwd,用攻击者账号 yangyangwithgnu 走完密码找回全流程。
输入用户名和图片验证码后提交:
验证为有效用户名后,系统提供手机、邮箱两种密码找回方式,选用邮箱方式:
登录邮箱查收重置验证码:
输入重置验证码:
进入新密码页面,输入后提交,拦截请求如下:
其中,PHPSESSID=dcusc1ahkn4ioqeeav9c6e0bdq、USER_ACCOUNT=yangyangwithgnu、 USER_APPID=1092 这三个参数引起我的注意。这个请求,用于重置账号 yangyangwithgnu 密码,那么服务端如何知道该重置 yangyangwithgnu 而不是 yangyangwithgnu2、yangyangwithgnu3 呢?刚才说的那三个参数中肯定有一个用于该目的。逐一尝试发现,PHPSESSID 就是它。
这让我闻到浓郁的 cookie 混淆的味道。大致攻击思路:首先,用攻击者账号 yangyangwithgnu 进入密码找回流程,查收重置验证码、通过校验;然后,输入新密码后提交,拦截中断该请求,暂不发至服务端,这时,PHPSESSID 关联的是 yangyangwithgnu 账号;接着,关闭浏览器的 burp 代理,新开重置流程的首页,在页面中输入普通账号 liuwei 后提交,这时,PHPSESSID 已关联成 liuwei 了;最后,恢复发送之前中断的请求,放至服务端,理论上,可以成功重置 liuwei 的密码。
用上述思路尝试将 liuwei 密码重置为 PenTest1024,前端显示重置成功:
尝试用 liuwei/PenTest1024 登录:
成功进入系统:
同理可重置管理员账号 administrator,为避免影响业务,不再实际操作。
案例二:通过篡改请求包中的用户名参数,实现重置任意用户密码。
密码找回页面 http://www.xxxx.cn/getpass.html,用攻击者账号走完密码找回全流程,涉及三步请求,依次为:验证用户名是否存在、获取短信验证码、提交短信验证码和新密码。第三步的请求拦截如下:
各参数作用从其命名可了解。尝试将 accountname 参数值篡改为普通账号 zhangzhiqiang 后放行,应答为:
重定向至登录页面。用普通账号 zhangzhiqiang/PenTest1024 登录成功。
查看个人信息:
泄漏用户手机号、邮箱等敏感信息。
查看视频监控设备列表:
视频监控设备登录信息:
登录后可查看实时视频监控,隐私考量,不截图了。
另外,密码找回流程第三步的请求中的 vcode 参数为短信验证码,单次有效,不可复用,如何实现自动批量密码重置?经测试,将该参数置空,或者完整删除该参数,服务端不再校验短信验证码。
综上,几个问题结合,可导致任意用户密码重置。
案例三:通过篡改带 token 的重置链接中的用户名,实现重置任意用户密码。
http://xx.xxxx.com/echannel/forgetPassword/ 为重置密码页面。攻击者用户 ID 为 42558。
输入攻击者账号绑定的邮箱后点击“确认”,收到如下带 token 的密码重置链接:
该重置链接有两个地方引起我的注意:一是,NDI1NTg= 为 base64 编码,解码为 42558,正是攻击者账号的用户 ID;二是,这是个 REST 风格的请求。所以,尝试用普通账号的用户 ID 的 base64 编码替换 NDI1NTg= 后,成功重置该普通用户的密码。
特别注意,参数部份出现 / 应想到这是 rest 风格的参数和参数值。
防御措施方面,一定要将重置用户与接收重置凭证作一致性比较,通常直接从服务端直接生成,不从客户端获取。另外,密码找回逻辑中含有用户标识(用户名、用户 ID、cookie)、接收端(手机、邮箱)、凭证(验证码、token)、当前步骤等四个要素,这四个要素必须完整关联,否则可能导致任意密码重置漏洞。另外,HTTP 参数污染、参数未提交等问题,服务端也要严格判断。
四、重置凭证未校验
这次,关注因重置凭证未校验导致的任意用户密码重置问题。
在日常对密码找回功能的攻击中,我的大部份精力聚焦在是否可以暴破验证码、是否可以劫持接收验证码的手机号或邮箱、是否可以混淆重置其他账号、是否可以绕过验证步骤、甚至是猜测重置 token 的生成规律等攻击方式上,反而忽略了最容易、最低技术含量的一种方式——服务端未校验重置凭证。换言之,不论你输入的重置验证码或密保答案是否正确,只要请求格式无误,均可成功重置任意账号密码。我举两个真实案例(漏洞均已修复,就不打码了),你感受下。
案例一:因服务端未校验 token 导致可重置任意账号密码
密码找回页面 http://www.omegatravel.net/users/retrievePassword/ 用攻击者账号 yangyangwithgnu@yeah.net 进入密码找回全流程,输入图片验证码后提交:
随后收到带 token 的密码重置链接的邮件:
其中,key:FqvICT 和 userEmail:yangyangwithgnu@yeah.net 引起了我的注意。正常来说,提交该 URL 后,服务端会校验 key 与 userEmail 是否匹配,若匹配则进入提交新密码页面,若不匹配则报错。现在,我尝试将 key 从 FqvICT 改为 xxxxxx 后再访问,本来心理预期将看到报错页面,没想到进入了新密码提交页面,难倒所谓的重置 token 仅仅是个摆设?
赶紧找个账号试试,就拿信息收集时找到的 travel24@omegatravel.net 为例(更多后台账号见后文)。参照前面收到的重置链接格式,简单拼装为 http://www.omegatravel.net/users/retrievePasswordReset/key:xxxxxx/userEmail:travel24@omegatravel.net,是滴,key 的值我随便写的,访问看看,哇,居然真的进入了新密码提交页面:
输入新密码 PenTest1024 后提交,网站提示“修改密码成功”。尝试用 travel24@omegatravel.net/PenTest1024 登录,成功进入系统:
如何获取其他账号?从注册页面可知,该网站只能用邮箱注册,邮箱即账号。我关心普通用户和内部员工用户两类账号(即邮箱)。普通用户的邮箱字典方面,把国人常见姓名拼音 top500 结合常见邮箱后缀(@qq.com、@163.com 等等)快速生成个简单邮箱字典;内部员工的邮箱方面,我从该网站域名注册信息查询到联系人为 omegait@omegauk.net,说明该公司使用 @omegauk.net 的邮箱后缀,同上,把国人常见姓名拼音 top500、常见后台账号,结合 @omegauk.net 邮箱后缀,快速生邮箱字典;另外,从“联系我们”、“诚聘英才”、“公司简介”等页面找到大量内部员工邮箱和合作伙伴邮箱,如 info@jadetravel.co.uk、info@ukchinese.com 等等:
将以上几类邮箱字典存为 mail.txt 也就是用户名。
这样,我不仅可以重置普通账号的密码,还能劫持大量内部员工、合作伙伴的账号,为避免影响业务,不再实际操作。
案例二:可枚举无密保的用户名,导致任意密保答案均可重置密码
在密码找回页面 http://www.hzpzs.net/u_findPassword.asp 输入有效用户名 yangyangwithgnu 后拦截请求包:
猜测该数据包用于用户名有效性校验,放至 repeater 中分析。
由于没用图片验证码,导致可枚举有效用户名,发现三类情况。一是,用户名存在且设置过密保问题,应答类似:
二是,用户名存在但未设置密保问题,应答类似:
三是,无效用户名,则应答类似:
用常见用户名和中国人姓名拼音作为字典进行枚举,在所有结果中过滤显示含有关键字 <td width=”65%”></td> 的应答,得到的所有 UserName 参数值即为未设置密保问题的用户名。如:aaron、admin、adolph、alisa、chenchen、esther、jones 等等。
按正常流程,对 chenxin 进行密码重置,输入任意密保答案均可重置密码:
加固措施
密码重置凭证一定要严格校验,空密保问题时禁止通过密保找回密码;服务端应限制枚举等恶意请求。