php session验证码为null_原创 | 记一次"发短信"造成的session覆盖越权

博客讲述了PHP应用中一个关于session验证码为null的问题,导致的用户信息越权访问。攻击者能通过操控短信验证码来改变session中的loginName,从而访问其他用户敏感信息,如卡号。流程包括利用忘记密码功能修改手机号,进而影响session,最终实现未授权访问。
摘要由CSDN通过智能技术生成
4daa23965f67d696f350bc6427f28101.gif 点击上方蓝字 关注我吧 相关背景 大大小小的网站都是由各式各样的业务功能组合而成的。例如登录、忘记密码、查看个人信息等。一般情况下,相关的业务凭证存在设计缺陷的话,会存在类似平行/垂直越权等安全问题。常见的业务凭证及越权场景有:
  • 用户身份id(userid,例如通过userid越权查看用户信息)
  • 对象id(fileid,例如通过fileid越权查看其他用户的备案资料(身份证、手机号、住址等))
  • 基于文件名(例如可以遍历时间戳组成的文件名获取用户订单)
  • Cookie中的字段(例如增加user=admin,即可垂直越权访问管理员模块)
  • Session中的内容(例如session覆盖)

一般在测试时,可以通过寻找相关的凭证,通过遍历、猜解等方式,进行业务逻辑缺陷的发掘。
前段时间审计某项目时发现一处结合忘记密码发送短信功能进行session覆盖越权的业务缺陷,下面是具体的发现过程。

挖掘过程 系统基于SpringMVC进行开发,在实际代码中,以查看用户个人身份信息为例,一般都会与session会话进行绑定,在登录成功后把对应的用户凭证保存在会话session中,当访问业务接口获取查看用户个人信息时,因为相关的业务凭证不是以userid这种可控的参数形式传递的,从一定程度上避免了越权的缺陷。
下面是登录过程的具体实现:
@RequestMapping(value = "/loginAuth")public String login(String loginName,String password,String captcha,HttpSession session){  String VerifyCode = session.getAttribute("captcha");  if(StringUtils.isBlank(loginName)){    return ResponseData.fail("用户名为空");  }  if(StringUtils.isBlank(password)){    return ResponseData.fail("password");  }  if(StringUtils.isBlank(VerifyCode)||StringUtils.isBlank(captcha)||!VerifyCode.equals("captcha")){    return ResponseData.fail("验证码错误");  }  //防止验证码复用问题    session.removeAttribute("captcha");    //登录逻辑    User user=userService.loginAuth(loginName,password);    if(user!=null){      session.setAttribute("loginName",user.getName());      ......    }else{      return ResponseData.fail("账号密码错误");    }}

大致流程如下,当一系列的合法性校验完成后,会将当前登录用户的用户名作为业务凭证存入到对应的session容器中:

75c8db3147817fbe45c050cf783ea176.png

通过相应的业务凭证就可以完成很多的安全措施了,例如通过拦截器防止接口的未授权访问:

String loginName=(String)session.getAttribute("loginName");if(StringUtils.isBlank(loginName)){    throw new Exception("Not logged in Exception");}
同样的以查询用户卡号信息接口为例,因为整个过程都是通过session容器中的业务凭证loginName来交互的,绑定了当前会话,无法越权查看他人的卡号信息:
@RequestMapping(value = "/getUserCard")public Object getUserCard(HttpSession session){  List<String> cardIds = userService.getUserCard(session.getAttribute("loginName"));  return CardService.getCardInfo(cardIds);}

上述过程梳理下来乍一看好像没法发现越权类的缺陷,若想越权查询其他用户卡号信息,思路也很简单,必须控制session会话中的loginName参数。

比较幸运的是,在忘记密码功能处,找到了对应的突破口。忘记密码功能一般通过结合短信验证码等方式进行密码的重置,同样的为了防止相关的验证码多用户可用,会跟当前会话进行绑定。保证当前请求的短信验证码仅仅可以供请求的手机号业务使用。 当前业务系统忘记密码模块获取短信的部分代码如下,可以看到第一步是传入需要重置密码的用户名,然后将该用户名loginName同样的存入到session容器中,这里应该是为了后面校验验证码使用的,但是不重要,已经得到想要的可控点loginName了:
@RequestMapping(value={"/getMessage"})@ResponseBodypublic Object getSecPwdQuestion(HttpServletRequest request, HttpServletResponse response,HttpSession session) throws Exception{  String loginName = request.getParameter(“loginName”);  if (StringUtils.isNotBlank(loginName)) {  session.setAttribute("loginName",loginName);  User user = this.UserService.getUserByLoginName(loginName);  if (user != null) {    //获取短信    ......  }else{    return ResponseData.fail("用户不存在");  }}

可以看到这里其实是可以通过发送短信的方式来控制session会话中的loginName参数的。联系前面登录过程以及获取用户卡号的过程,这里利用思路就出来了,首先通过忘记密码模块的/getMessage接口进行请求,通过变化对应的需要忘记密码的手机号设置对应的loginName,然后再访问/getUserCard接口,即可越权获取对应用户的卡号信息了,具体流程如下:

2cc70efb0fff8366012b010692863dd8.png

拓展延伸 整个利用过程有点类似session覆盖,但是覆盖的方法是通过跨业务模块进行操作的。关键点还是业务凭证在两个业务功能点都是用到了,并且在忘记密码模块用户可控。
因为HTTP是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息),每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。
所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过Cookie或者Session 去实现。
那么很多时候都会在session中存入对应的业务凭证进行相关的业务维护,那么在实际黑盒/白盒测试中,可以通过分析猜测对应的代码实现,梳理每个模块共通的业务凭证(例如这里的loginName),可能会有意外的惊喜。 f2b1e594c19c467022236689476d919d.gif 4fc3ef9668eff42a20651c3e0aeac206.pngwww.sec-in.com在这里,探索技术与热爱-扫码关注我们- dcfe1cb387d343c971e1d6651973ce0c.gif cfe3ed5962619ede169da822caf0bfcc.gif 点分享 e9c3a9dd92fed0b36e33bac0ebb83cc4.gif 点点赞 3fb1115f1fc28f58abafaf15a58a96d2.gif 点在看
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值