Loren麟
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

Owasp top 10 最新排名
最新的2021 top 10已经出来了,我们从A01开始进行一次详细解读,本系列会详细介绍各个漏洞的变化与内容,并会着重介绍新增的漏洞情况。
A01 Broken_Access_Control (失效的访问控制)
因素
CWE映射 | 最大发病率 | 平均发病率 | 平均加权利用率 | 平均加权影响 | 最大覆盖范围 | 平均覆盖率 | 总发生次数 | 总CVE |
---|---|---|---|---|---|---|---|---|
34 | 55.97% | 3.81% | 6.92 | 5.93 | 94.55% | 47.72% | 318487个 | 19013年 |
概述
从第五位开始,94%的申请都进行了某种形式的中断访问控制,平均发生率为3.81%,在贡献的数据集中出现的次数最多,超过318k。值得注意的共同弱点列举(CWE)包括CWE-200:将敏感信息暴露给未经授权的参与者,CWE-201:在发送的数据中插入敏感信息,和CWE-352:跨站点请求伪造。
说明
访问控制强制执行策略,这样用户就不能在他们想要的权限。故障通常会导致未经授权信息披露、修改或销毁所有数据或执行超出用户限制的业务功能。公共通道控制漏洞包括:
违反最低特权原则或默认拒绝,如果只允许特定能力访问,角色或用户,但对任何人都可用。
通过修改URL(参数)绕过访问控制检查篡改或强制浏览),内部应用程序状态,或或者通过使用攻击工具修改API请求。
通过提供其唯一标识符(不安全的直接对象引用)。
访问API时缺少POST、PUT和DELETE的访问控件。
特权的提升。在没有登录或以用户身份登录时充当管理员。
元数据操作,例如重放或篡改JSONWeb令牌(JWT)访问控制令牌,或cookie或隐藏字段操纵以提升特权或滥用JWT的无效性。
CORS错误配置允许未经授权/不可信的API访问起源。
强制以未经身份验证的用户身份浏览已验证的页面,或以标准用户的身份访问特权页。
如何预防
访问控制仅在受信任的服务器端代码或无服务器API,攻击者无法修改访问控制检查或元数据。
除了公共资源,默认情况下拒绝。
实现一次访问控制机制并在整个过程中重复使用应用程序,包括最小化跨源资源共享(CORS)使用。
模型访问控制应该强制执行记录所有权,而不是接受用户可以创建、读取、更新或删除任何记录。
唯一的应用程序业务限制要求应由领域模型。
禁用web服务器目录列表并确保文件元数据(例如。,.git)和备份文件不在web根目录中。
记录访问控制失败,在适当的时候提醒管理员(例如。,反复失败)。
速率限制API和控制器访问以将自动攻击工具。
注销后,服务器上的有状态会话标识符应失效。无状态JWT令牌应该是短期的,这样攻击者的机会被最小化。对于寿命较长的jwt,强烈建议遵循OAuth标准撤消访问。
开发人员和QA人员应包括功能访问控制单元以及集成测试。
攻击场景示例
场景1:应用程序在SQL调用中使用未经验证的数据正在访问帐户信息:
pstmt。setString(1,request.getParameter(“acct”));结果集结果=pstmt。executeQuery();
攻击者只需修改浏览器的“acct”参数即可发送不管他们想要什么账号。如果未正确验证,则攻击者可以访问任何用户的帐户。
https://example.com/app/accountInfo?acct=notmyacct
场景2:攻击者只需强制浏览目标URL。行政访问管理页需要权限。
https://example.com/app/getappInfohttps://example.com/app/admin_getappInfo
如果未经身份验证的用户可以访问任何一个页面,则这是一个缺陷。如果非管理员可以访问管理页面,这是一个缺陷。
失效的访问控制(越权漏洞)
在我的理解中所谓的失效访问控制就是越权漏洞,也就是当开发者的逻辑出现问题时出现的逻辑问题。
越权漏洞是发生在网站,APP功能里的,比如用户登录,操作,提现,修改个人资料,发送私信,上传图片,撤单,下单,充值,找回密码等等,那么可以简单的理解为,绕过授权对一些需要验证当前身份,权限的功能进行访问并操作,
举例来讲:在网站APP里的找回密码功能,正常是按照手机号来进行找回密码,那么如果存在越权漏洞,就可以修改数据包,利用其它手机号获取短信,来重置任意手机号的账户密码。
发生漏洞的根本原因是对需要认证的页面存在漏洞,没有做安全校验,导致可以进行绕过,大部分的存在于网站端,以及APP端里,像PHP开发的,以及JAVA开发,VUE.JS开发的服务端口都存在着该漏洞,小权限的用户可以使用高权限的管理操作,这就是越权漏洞。
越权漏洞又分为水平越权,垂直越权,
水平越权
水平越权漏洞是可以操作同一个层次的账号权限之间进行操作,以及访问到一些账号敏感信息,比如可以修改任意账号的资料,包括查看会员的手机号,姓名,充值记录,撤单记录,提现记录,注单记录等等,也可以造成使用水平越权来执行其他用户的功能,比如删除银行卡,修改手机号,密保答案等等。
水平越权常见于业务系统中,对用户信息或者订单信息进行增删改查操作时,由于用户编号或者订单编号有规律可循(有序递增,订单编号常发现以日期开头后面再接几位有序增长的数字,类似20200520xxxx1,20200520xxxx2),测试人员通过burpsuite的 intruder对目标参数进行遍历测试即可
垂直越权
垂直越权漏洞可以使用低权限的账号来执行高权限账号的操作,比如可以操作管理员的账号功能。
当我们利用总不能用记的身份凭证来执行管理员的功能时,如果管理者没有很好的权限管理就会出现这种情况。多见于接口功能上,因为这样的访问点并不能很容易的被用户发现,当我们通过某些攻击手段获取了一个管理员的功能接口时,我们把身份认证的cookie(也有可能是其他字段或内容)替换为普通用户的就有可能实现攻击了,一般是利用BP中的Repeater来实现。
越权漏洞的探测
通常情况下,一个 Web 程序功能流程是登录 - 提交请求 - 验证权限 - 数据库查询 - 返回结果。如果验证权限不足,便会导致越权。常见的程序都会认为通过登录后即可验证用户的身份,从而不会做下一步验证,最后导致越权。
1. 通过隐藏 URL
实现控制访问有些程序的管理员的管理页面只有管理员才显示,普通用户看不到,利用 URL 实现访问控制,但 URL 泄露或被恶意攻击者猜到后,这会导致越权攻击。
2. 直接对象引用
这种通过修改一下参数就可以产生水平越权,例如查看用户信息页面 URL 后加上自己的 id 便可查看,当修改为他人的 ID 号时会返回他人的信息,便产生了水平越权。
3. 多阶段功能
多阶段功能是一个功能有多个阶段的实现。例如修改密码,可能第一步是验证用户身份信息,号码验证码类的。当验证成功后,跳到第二步,输入新密码,很多程序会在这一步不再验证用户身份,导致恶意攻击者抓包直接修改参数值,导致可修改任意用户密码。
4. 静态文件
很多网站的下载功能,一些被下载的静态文件,例如 pdf、word、xls 等,可能只有付费用户或会员可下载,但当这些文件的 URL 地址泄露后,导致任何人可下载,如果知道 URL 命名规则,则会便利服务器的收费文档进行批量下载。
5. 平台配置错误
一些程序会通过控件来限制用户的访问,例如后台地址,普通用户不属于管理员组,则不能访问。但当配置平台或配置控件错误时,就会出现越权访问。
越权漏洞的修复
建议做一个过滤器,对权限进行全局校验(每次调用某个接口时,可先对权限进行校验)。大体流程是:第一步清洗URL地址,并提取Api接口名称;第二步从session中提取当前登录用户的userid;第三步提取当前用户的角色id;第四步判断当前用户对应的角色是否有权限访问当前Api接口(检查垂直越权);最后判断当前登录用户是否对目标对象有操作权限(检查水平越权)。
首先是获取URL地址中的Api接口名称,这里对URL地址进行了一次URL解码,还是存在绕过的风险,因为当用户%2523时,就可能绕过。
public static String GetApiName(String Url) throws Exception { String DecodeUrl = URLDecoder.decode(Url,"UTF-8");URL url = new URL(DecodeUrl);String ApiUrl = url.getPath(); if(ApiUrl !=null){ String[] ApiPath = ApiUrl.split("/");String ApiName = ApiPath[ApiPath.length-1]; return ApiName;} return null;} |
获取userid时,建议从session中获取,而不是在cookie中再新建一个userid字段,用于标识用户身份。
HttpSession session = ServletActionContext.getRequest().getSession();String userId = session.getAttribute("userId");*/ |
从session中提取userid后,就要查询与之对应的角色id
//UserId为用户id,RoleId为角色ID,通过UserId获取roleidpublic static int GetRoleId(int UserId) { Connection conn = Connect();PreparedStatement st = null;ResultSet rs = null; int RoleId = 0; try { // 查询接口访问的角色id,roleid为权限表里的角色ID字段,apiname为权限表里的API接口名称IDString sql = "select roleid from usertable where userid = ?";st = conn.prepareStatement(sql);// 这里使用PreparedStatementst.setInt(1, UserId);rs = st.executeQuery(); if(rs.next()){ RoleId = rs.getInt("roleid"); return RoleId;} } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("查询失败!");} return RoleId;} |
校验垂直越权时,判断当前用户是否对指定的接口有访问权限,U_RoleId为用户名对应的角色id,A_RoleId为Api接口对应的角色id,Api_Name为用户尝试访问的API接口名称(这里在系统架构评审,安全设计阶段,就要检查数据库的权限表设置时,Api接口是否有指定对应的角色id)
public static boolean CheckUpPrivilege(int UserId, String Api_Name) { Connection conn = Connect();PreparedStatement st = null;ResultSet rs = null; int U_RoleId = GetRoleId(UserId); int A_RoleId = 0; try { String sql = "select roleid from user_role where apiname = ?";st = conn.prepareStatement(sql);// 这里使用PreparedStatementst.setString(1, Api_Name);// 执行sql命令roleid和Role_Id,判断用户是否有权限访问对应的接口地址rs = st.executeQuery(); if(rs.next()) { A_RoleId = rs.getInt("roleid");// 通过比较,当用户角色id大于等于接口指定的角色id是,可以访问,部分特定接口只有指定的角色才能访问,可直接限定if (U_RoleId >= A_RoleId) { return true;} else { return false;} } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("查询失败!");} return false;} |
最后再判断一下是否是水平越权,S_UserId为当前登录用户的userid,P_UserId为目标对象对应的userid,比如对订单信息进行操作时,可以先通过订单号提取与之对应的userid,再进行判断(当然,订单表,在系统架构评审,安全设计阶段,就要检查订单号是否有指定对应的用户id)。
public static boolean CheckLevelPrivilege(int S_UserId, int P_UserId) { if(S_UserId == P_UserId){ return true;} else{ return false;} } |
结尾
逻辑问题是很难通过机器扫描发现的,所以一但发现就会变成高危漏洞,对目标系统造成较高的影响。但相对的发现也比较困难,要求测试人员的思路清晰,并且可以把自己真正的代入到正常的使用者身份中去。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)