任意用户漏洞挖掘分享
这是一次在工作当中遇到的比较搞笑的任意用户登录漏洞,简单的记录一下,也结合了一下近期的任意登录案例进行一个复盘,旨在自身在开发过程中应注意代码规范,防患于未然。
登录框checklist
遇到登录框必检查的checklist
:
- 用户枚举:未统一返回的内容
- 弱口令:通过信息收集暴力破解
- 空口令:将密码置空发送数据包
- 登录认证绕过:通过fuzz找特殊接口
- 暴力破解风险:网站无验证码,或不设置页面重定向跳转且密码为明文或弱加密
- 验证码复用:验证码不失效
- 测试数据:特殊手机号(13333333333…)及验证码(000000-999999)
- 验证码绕过:修改登录的返回包等
- 短信验证码可暴力破解
- 短信验证码可预测
- 短信炸弹
- 恶意锁定问题
- 帐号密码明文传输
- SQL注入:万能密码等
- SSO认证缺陷:越权登录他人帐号
- 任意用户漏洞
- ……
总之就是存在登录框的地方,什么漏洞都是可能存在的,这也是在面试当中经常会被问到的。今天的案例是与任意用户相关的,着重点就放在任意用户登录上。
案例1:用户枚举演变任意用户
初探
这是一个工作的APP目标,打开APP后,但大致功能如下(图文无关)
最开始是最原始的用户枚举,选择帐号登录,输入任意帐号密码,如果帐号存在会返回密码错误,帐号不存在则返回其他内容
后面想测试登录处有无短信炸弹、密码暴力破解等漏洞,这里的手机号参数是经过DES加密的,通过反编译安装包,成功拿到秘钥
尝试了在手机参数中加入空格,+点等特殊符号,都是不能通过后端对手机号格式的验证
第一个周就这样浅浅的水一下,交了一个用户枚举
再探
又是朴实无华的一个周,依旧来到了登录框处进行对抗
经过一番尝试,在登录的手机号处添加了一个空格后再进行DES加密得到最终发送的参数,发送数据包,登陆成功!!!
但是我心中还是有疑问的,为啥上周这个方法都还不行,这周用同样的方法就登录成功了,也和其他同事讨论了一下出现该问题的原因,因为之前他们用同样的方法是无法登录的
缘由
为什么在第一周测试的时候该方法无法任意用户登录的,第二周就能登录原因?通过和开发沟通,大致得知了原因,不过师傅们也可以先脑补下是因为什么原因,修复用户枚举后的部分代码如下:(已脱敏简化)
@RequestMapping(value = "/login.ajax")
@ResponseBody
@InterfaceLogAnno("/user/login.ajax")
public JsonIndivUserLoginResp login(HttpServletRequest request, HttpServletResponse response) {
……
//密码登录
if (!StringUtils.isEmpty(mobilePhone) && StringUtil.isMobileNo(mobilePhone) && !StringUtils.isEmpty(password)) {
//密码登陆逻辑判断 这部分代码没什么问题
……
}
//验证码登录
if (!StringUtils.isEmpty(mobilePhone) && StringUtil.isMobileNo(mobilePhone) &&
!StringUtils.isEmpty(verifyCode) && verifyCode.length() == CODE_LENGTH) {
//验证码登录逻辑判断 这部分代码没什么问题
……
}
/**
* 登录就给最新的token
*/
newToken = createToken(mobilePhone, deviceID, queryResult, token);
}
应该有代码基础的师傅,看见这部分代码,就能看出问题所在了,不管是密码登录还是验证码登录都需要if语句里面的条件都为真才会走到对应的逻辑进行判断,但当我们请求的手机号参数不符合规范时,StringUtil.isMobileNo(mobilePhone)
返回的是false
,即不会走密码登录和验证码登录的逻辑就直接来到了获取token
的地方
最后的修复是在代码开始的部分判断了手机号格式,不符合就进行返回
@RequestMapping(value = "/login.ajax")
@ResponseBody
@InterfaceLogAnno("/user/login.ajax")
public JsonIndivUserLoginResp login(HttpServletRequest request, HttpServletResponse response) {
//校验注册信息-用户名和密码格式校验
if (StringUtils.isEmpty(mobilePhone) || !StringUtil.isMobileNo(mobilePhone)) {
logger.info("------------------手机号无效----------------------");
return new JsonIndivUserRegisterResp(Constant.JSON_HTTP_USER_PWD_STATUS_FAILED, "用户名或密码错误!");
}
……
}
这种方式还是不够优雅,不过就“能跑就行”的原则来说,这样就够了
案例2:偶然的任意用户
这是一个众测项目,拿到资产已经很晚了,用户枚举短信轰炸这种水洞要嘛是重复要嘛就是不接收,也是一个登陆框开局
(要说明一下虽然这是按照step1-step3步骤重置密码的,但这个地方是不能跨越步骤的)
在首页的登录忘记密码处,直接常规的用两个手机帐号A、B,用B手机号接收验证码去重置A手机号,这样确实很简单,所以直接重置失败!!!
后面在一次偶然中,我发送了两次手机验证码,即A手机号发送,B手机号也发送,再用B手机号去接收验证码重置A手机号
这次只能猜测大致的原因是在后端验证时,验证码和手机号只做了弱绑定,即分开验证手机号和验证码是否存在缓存当中。
完结
其实还有几个之前在src中遇到的任意用户案例,时间比较久远了,下次再进行分享。
免责声明
免责声明:本博客的内容仅供合法、正当、健康的用途,切勿将其用于违反法律法规的行为。如因此导致任何法律责任或纠纷,本博客概不负责。谢谢您的理解与配合!