前言
此系列已经更新三篇,在第一篇文章被精选后,很多读者添加了我的联系方式,给了我鼓励,也给了我建议。我都会采纳,吸取经验。也感谢很多企业能联系我给我一个就职的机会,你们一直是我继续写下去的动力,谢谢,我们一起成长。
概述
今天这篇文章将会讲到逻辑漏洞之短信漏洞,此文章预示着接下来的思路将需要开发的基础,拥有代码的阅读能力。本文会从发送短信开始,揭示可能存在的漏洞点,以及如何修复预防这样的漏洞。希望对读者有所收获。
短信轰炸
发短信存在的功能点通常是注册,登录,找回密码。但很多企业实现这些功能点经常存在短信轰炸漏洞。所谓的短信轰炸漏洞就是用户能无限获取验证码,一定程度上给用户带来骚扰的可能,另一个程度上可能会给企业带来财务损失(短信是与第三方运行商进行合作,需要付费)。
下面来看代码
/** * 获取验证码 */ @RequestMapping("/sendCode.json") public String sendCode(String tel) { //发送短信验证码 Boolean isSend = sendSmsTools.send(tel); //发送验证码核心函数 if (isSend) { return SuccessJson("发送成功"); } else { return ErrorJson("发送失败"); } }
上图为发送验证码的具体细节,因为使用了阿里云的短信服务,所以采用了阿里云API进行实现,那么就在上面两幅图中可以看到,前端在调用发送短信API时,调到Controller接口,然后内部调用发送验证码接口直接对接阿里云API。我们来站在宏观角度来讲,如果用户抓取到前端请求后端调用后端发送短信的api后,反复调用,使用burp重放此包也好,或者渗透者编写网络请求调用也好。都会在后端反复调用这个接口,从而造成了短信轰炸。
防御:
我们分析下整个过程
1.前端请求后端发送短信API
2.短信方法调用阿里云短信服务进行验证码发送
3.获得回调结果
在这个过程中,第二部是调用阿里云我们不可控,只能在第一步和第二部之间解决这个问题。下来看解决思路
public Boolean send(String phone){ String redisPhone=redisService3.getStr(phone); //引入第三方存储redis获取此手机号的获取记录 if(redisPhone!=null){ //如果不为空则获取过,然后驳回 return false; } DefaultProfile profile=DefaultProfile.getProfile("cn-hangzhou",ACCESSKEYID,ACCESSKEYSECRET); IAcsClient client=new DefaultAcsClient(profile); CommonRequest request=new CommonRequest(); request.setMethod(MethodType.POST); request.setDomain("dysmsapi.aliyuncs.com"); request.setVersion("2017-05-25"); request.setAction("SendSms"); request.putQueryParameter("PhoneNumbers",phone); request.putQueryParameter("SignName","xxx"); HashMap<String, Object> param = new HashMap<>(); String code = String.valueOf(new Random().nextInt(899999) + 100000); param.put("code", code); request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param)); request.putQueryParameter("TemplateCode", "SMS_200693347"); try{ CommonResponse response=client.getCommonResponse(request); if(response.getHttpResponse().isSuccess()){ redisService3.setStrAsMINUTES(phone + "_code", code, 15); //如果获取成功在redis中存入此手机号获取记录 return true; }else{ return false; } }catch(Exception e){ e.printStackTrace(); return false; } }
我举例其中一种解决办法,引入redis。redis作为NoSQL数据库,内存级数据库,效率快。我们在获取短信验证码之前,在redis中读取当前手机号在redis中的获取记录。如果获取记录为空则证明为获取过,则往下执行。如果获取记录不为空则返回false进行驳回。
但是在获取短信验证码之后,如果阿里云告诉我们发送成功之后,我们将此手机号的获取记录存入redis,以便下回可以读取到记录。
叙述一遍这样的流程
1.前端请求后端发送短信API
2.短信方法读取用户是否已经获取过验证码,判断后续是否继续执行
3.短信方法调用阿里云短信服务进行验证码发送
4.如果发送成功,将获取记录存入redis
总结:相信各位看到这里,在审计的过程中了解对方是否在后端被调用时并未做判断处理就可以知道是否存在短信轰炸了。
短信越权
之所以叫这个名字,是我一时之间想不出一个专业名词能形容这个问题,请大家见谅。
我说一种场景,当渗透者在对一个不知道身份的用户使用短信验证码爆破猜解的时候,例如登录接口并未做验证码暴力破解的限制,而在修改密码时进行了验证码错误次数限制。
下面看流程
1.前端调用后端发送短信
2.用户收到验证码点击登录按钮
3.登录接口判断登录验证码是否成功
4.进入系统
这样的一个流程会出现一个什么问题呢,如果验证码成功进入系统开发者如果没有将使用过的验证码进行删除,那么在修改密码时,渗透者直接将登录爆破出来的验证码在修改密码处使用,绕过了修改密码的验证码错误限制。这样的一个思路就导致了验证码移位使用,验证码错误次数绕过等限制。
下面给出正确流程
1.前端调用后端发送短信
2.用户收到验证码点击登录按钮
3.获取当前手机号在redis中的存储的验证码
4.登录接口判断用户输入验证码与redis存储验证码是否相同
5.删除当前手机号在redis中存储的验证码
6.进入系统
所以使用过的验证码一定要进行删除操作,以免为后续带来不必要的麻烦,结合上文给出删除操作
public Boolean clearCode(String phone){ redisService3.del(phone+"_code"); return true; }
在redis中将其删除。
最后一个知识点,怎么能避免渗透者持续暴力猜解用户验证码,其实答案很简单,在接口处添加签名算法。每次调用后台接口都有一个唯一的参数,作为前后端调用的规则。重放攻击只会对一个包进行重放,第二次进行请求并不会满足唯一的参数条件。
但是上文前端审计中讲到如何破解签名算法,是的,攻防只是在提高门槛。我的文章具有连贯性,希望读者可以从头开始阅读。
结尾
本文讲了短信方面存在的漏洞,审计以及防御的思路。在危害等级中偏低,但也是不可或缺的一环,学习就是由简入难的过程,大家继续关注吧~
来源:freebuf.com 2020-11-04 17:41:28 by: 冬雪在线挖洞
请登录后发表评论
注册