您还未登录! 登录 | 注册 | 帮助  

您的位置: 首页 > 软件测试技术 > 其他相关 > 正文

越权漏洞之测试与修复

发表于:2020-07-01 作者:freebuf01 来源:freebuf

这几年,开发人员的安全意识与安全开发水平都有了很大程度的提高。像文件上传、SQL注入漏洞,在网络安全比较受重视的企业,差不多都已经杜绝了,即便有出现,也是凤毛菱角了。目前,部分企业网络安全面临的另一大挑战就是越权漏洞,特别是大型系统,功能模块接口特别多,如果未对权限进行全局校验,则难免会有所遗漏,最终出现越权漏洞。

一、越权漏洞定义及分类

越权,从字面意思不难理解,主要有以下几种可能:

1、未授权访问:本来没有账号(即没有某个功能权限),但是通过越权操作,获取了某个功能权限;

2、水平越权:本来有个账号(即只能操作自己的数据,比如增删改查),但是通过越权操作,能操作其他同等权限账号的数据。

3、垂直越权:本来有个账号只有小权限,但是通过越权操作,获取了大权限。

二、测试方法

2.1、未授权访问

最简单直接的测试方法是:不登录用户账号,直接访问要测试的功能模块(正常情况是需要登录才能访问),如果能正常访问,则说明存在漏洞。

修改返回包的测试方法:日常测试时,有些开发人员在前端校验返回包中某个参数的值,在返回包中有类似status=false的值,可将改为status=true(也有碰到过code=-1改为code=0或者1的情况);遇到过最奇葩的一次是:不登录账号直接访问那个系统的后台首页index.html时,返回包主体中通过js脚本,通过script和location跳转到登录页面login.html,通过burpsuite拦截响应表,再将那段用于页面跳转的js脚本删除,就能进入后台。(虽然这种修改返回包的方法最后也没有登录账号,但是进入后台后,能访问一些内部公共的敏感功能模块和信息)。

部分系统,用户信息并不是从sessionid中获取登录账号;而是在cookie中添加一个类似userid的键值对,再从中识别用户。从而可以尝试通过cookie伪造的方法进行测试。

2.2、水平越权

水平越权常见于业务系统中,对用户信息或者订单信息进行增删改查操作时,由于用户编号或者订单编号有规律可循(有序递增,订单编号常发现以日期开头后面再接几位有序增长的数字,类似20200520xxxx1,20200520xxxx2),测试人员通过burpsuite的 intruder对目标参数进行遍历测试即可

2.3、垂直越权

日常安全测试时,大多发现是未授权访问和水平越权。只碰到过一次将cookie中的isadmin=0改为isadmin=1,获取到管理员权限。苦于黔驴技穷,有幸某天灵光一现,想到测试任意文件上传和下载漏洞时,都能借助../和./进行绕过,垂直越权时,何不也尝试借助../和./进行绕过呢,最终果然发现新大陆。最后经过反复测试发现以下几种垂直越权测试方法。

http://www.xxxx.com/admin//admin.do

http://www.xxxx.com/admin/admin.do%23

http://www.xxxx.com/js/../admin/admin.do

http://www.xxxx.com/admin/./admin.do

三、修复方案

建议做一个过滤器,对权限进行全局校验(每次调用某个接口时,可先对权限进行校验)。大体流程是:第一步清洗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;}
  }

四、最后

类似订单号这种参数,生成时要无规律可循,可以通过hash算法进行加密;或者请求的数据包额外附带一个参数,比如token,从而防止重放和遍历订单号这类攻击(篡改订单号)。