从开发的角度看权限漏洞的最后一篇了,也就是数据权限篇。
0x00
相比于之前的未授权《一文理解权限类漏洞产生的原因之未授权篇》与功能权限《权限类漏洞解析——功能权限篇》,数据权限实现起来就麻烦了,也是最容易出错的,因为数据权限的通用实现很复杂,它的场景太多了。
具体哪里复杂我就不细说了,要不然几篇文章都写不完,反正就是它很难实现像功能权限那样的简单通用处理逻辑,所以很多开发是直接在接口里面去判断的,然而,做得越多,错得越多。
实现
还是以之前的订单功能为例,如果我需要查询订单,那我肯定只能查询自己的,那意味着,我保存的订单信息里面,最少要有订单创建人这个属性,那实现一个查询订单列表可能是这样的:
#略过了功能权限校验
def order_list():
# 获取当前登录用户
user_info = get_current_user()
# 根据当前用户查询所有订单信息
return get_orders_by_user(user_info.id)
好像也很简单哦?实际上也是,几乎不太可能出错,因为错误太明显了,就算开发实现错了,测试阶段也会被发现,不可能发布到线上的。我们实际在挖洞的时候,这种列表也几乎没什么操作的空间,最多看看是不是返回的数据是不是包含敏感信息了。
那如果是查询订单详情呢?可能会这样实现:
#略过了功能权限校验
def order_detail(order_id):
order_info = get_orders_by_id(order_id)
user_info = get_current_user()
# 这里要判断订单的用户ID是否和当前用户ID匹配
if order_info.user_id != user_info.id:
return 'no permission'
return order_info
几乎 90% 的数据越权漏洞,都是因为在实现的时候没有后面那一步操作,也就是判断数据的用户归属信息。为什么他这么容易出现?以我的经验来看,主要可能是:
1、开发没有安全思维,觉得既然列表只返回当前用户的订单了,那在查询详情的时候,肯定是列表中的数据,就没有必要再判断了。即只考虑正常场景,没有考虑异常场景。
2、疏忽了,这太常见了。因为像涉及到这种根据ID进行操作的接口,都需要进行对应的判断,查询判断了,删除呢?更新呢?如果没有对数据权限进行充分的设计,那就一定会出现问题。
漏洞发现
那像这种漏洞又如何去找呢?一个一个去找当然没有问题,看到参数中带有ID的就试一试改ID,但是怎么说呢,我更喜欢自动化,同样可以使用和测试功能权限一样的方法:
-
建两个普通用户A和B。
-
找到对应的鉴权方式,比如是使用cookie或者是某些请求头。
-
开启bp插件,功能是替换指定的请求头,并重放我的所有请求。
-
配置鉴权相关的请求头,将他们全部替换为用户B的。
-
使用用户A登录并点一遍所有功能。
-
过滤所有接口参数中带有id字样的重放请求,看是否有成功返回数据的,因为理论上应该全部失败,因为重放的接口是使用B的鉴权参数访问A的数据。如果有成功,则认为有越权的可能。
同样的,这里的BP插件是我自己写的。你也可以去github上搜索类似的插件,挺多插件是可以实现这个操作的。
0x01
虽然看起来好像挺简单的样子,但是不开玩笑的说,我80%以上的高危漏洞来源于它。我遇到的只要是这类越权,最低中危,最高严重。而且一但遇见了自增ID之类的可遍历数据权限越权,那几乎都是4位数以上(除了某些SRC)。所以了解一下它的产生原因及实现还是有必要的,你知道它怎么实现的,才知道如何更好的去挖掘。
欢迎关注我的公众号“混入安全圈的程序猿”,更多原创文章第一时间推送!