Web安全基础篇——越权漏洞

越权漏洞

越权漏洞是当前Web系统中常常存在的安全漏洞,该漏洞由于Web系统对用户的操作权限没有进行验证,或者验证并不充分而导致。

越权漏洞根据越权对象的不同可以分为水平越权和垂直越权。在一个站点内,常常具有普通用户和管理员用户。(有的站点还可能具有其他等级的用户)所谓水平越权,就是指同一等级的用户之间,可以冒充对方身份而进行一些操作;所谓垂直越权,就是指普通用户可以执行管理员用户的一些操作。除了水平越权和垂直越权外,还有一种类似的漏洞,即未授权访问,即在系统中有时我们可以想办法替换认证信息,达到访问其没有权限访问信息的漏洞。另外,还有上下文越权,上下文越权就是说在某个程序需要执行n个步骤,而你却可以跳过其中某个步骤,直接到最后的步骤。

越权漏洞点

​ 从Web系统功能点上来考虑越权漏洞,越权漏洞常常在一些含有“身份”的操作中出现,例如转账、发送消息、查看/修改个人信息、下单购物等等。如果我们要挖掘越权漏洞,那么可以着重关注一下这些功能点。如果Web系统没有对用户的身份进行验证的话,那么就有可能造成越权漏洞。

  1. 基础参数
    通过修改一下参数就可以产生水平越权,例如查看用户信息页面 URL 后加上自己的 id 便可查看,当修改为他人的id号时会返回他人的信息。再比如cookie中的参数就作为用户的凭据,修改这个凭据便可以其他用户身份通过验证。

  2. 多阶段验证
    多阶段功能是一个功能有多个阶段的实现。例如修改密码,可能第一步是验证用户身份信息,号码验证码类的。当验证成功后,跳到第二步,输入新密码,很多程序会在这一步不再验证用户身份,导致恶意攻击者抓包直接修改参数值,导致可修改任意用户密码。

  3. 基于参数的访问控制
    有的程序会在参数里面进行权限认证。如:www.xxx.com/uid=test&admin=0 ,把0改为1就有了admin权限。

  4. 链接隐藏
    有的程序会把页面独立,让爬虫爬取不到,但是可以使用扫目录的方式扫到url,如果此时页面不做权限认证,就可直接访问到功能点,或者只是前端跳转,可以使用burp抓回包,然后删除js代码绕过。

越权漏洞风险

Web越权漏洞属于较为严重的漏洞,并且在各大Web系统中也出现的较为频繁。在各大SRC中,基本上都会收录越权漏洞,并且常常被评价为中危甚至高危。然而,越权漏洞对于网站权限的获取却并不会有很大的帮助,特别是水平越权漏洞,其危害主要是在于危害了其他用户的信息安全,但是并不会对Web系统渗透攻击提供帮助。

越权漏洞修复

针对越权漏洞,主要是在Web开发时注意相关逻辑处理流程,具体包含以下方面:

  1. 权限验证采用双重验证方式,前端和后端同时对用户输入的信息进行校验。
  2. 在进行关键操作前和涉及到“身份”的相关操作前,需要对用户进行身份验证,检验用户是否具有相关权限。
  3. 对id等与身份和权限有关的敏感数据进行加密或其他特殊处理。
  4. 永远不要相信用户的输入,对于用户可控参数进行严格的检查和过滤。

filter设计缺陷导致的权限绕过

一般来说,为了防止越权操作,通常会结合filter进⾏相关接⼝的鉴权操作。其中不不外乎就是对每⼀个接口(通俗来说就是我们的URI/URL)进行业务梳理,然后判断当前URI/URL是否具有相应的业务权限。

一般情况下,通常是获取到当前URI/URL,然后跟需要鉴权的接口进行⽐对,或者直接结合startsWith()或者endsWith()方法,设置对应的校验名单。但是,在Java中获取当前request中的URI/URL通常会使用request.getRequestURL()request.getRequestURI()这两个方法,但是如果没有进⾏相关的处理的话,有可能导致权限控制绕过的风险。

绕过方式

当权限过滤器获取当前request中的URI/URL使用request.getRequestURL()request.getRequestURI()这两个方法时,可以考虑以下三种⽅式进行权限绕过。

非标准化绕过

例如/system/login开头的接口是白名单,不需要进行访问控制(登陆页面所有人都可以访问),其他接⼝都需要。

1
2
3
4
5
6
7
8
9
10
11
12
13
String uri = request.getRequestURI();
if(uri.startsWith("/system/login")) {
//登陆接口设置⽩白名单
filterChain.doFilter(request, response);
}
else if(uri.endsWith(".do")||uri.endsWith(".action")) {
//检测当前⽤户是否登陆
User user =(User) request.getSession().getAttribute("user");
if(user==null|| "".equals(user)) {
errorResponse(response, paramN, "未授权访问");
return;
}
}

相关原理:

中间件在进⾏解析时,会对我们URI中的../进行相关处理从⽽得到相关的servlet。也就是说尝试对我们访问的URL引入../,中间件是可以正常解析并完成正常业务的,以tomcat中的examples目录中的案例servlet访问为例,尝试访问一个不存在的目录login,然后通过../回到正常目录下,是可以正常解析的。

使用request.getRequestURL()request.getRequestURI()这两个方法进⾏访问接口的获取时,是不会对类似../等进⾏规范化处理的。

可以通过在URI中写⼊/login/../,使得权限过滤器认为我们当前访问的接⼝为白名单接口,从而绕过权限控制,使得系统认为我们当前访问的接⼝是登陆login,不需要进行权限校验。

1
/system/login/../UserInfoSearch.do

URL截断绕过

例如/system/login开头的接⼝是⽩名单,不需要进行访问控制(登陆⻚面所有人都可以访问),其他接口都需要进行登陆检查,防止未授权访问,但是考虑到了../的非法访问问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
String uri = request.getRequestURI();
if(uri.contains("./")){
errorResponse(response, paramN, "非法访问");
return;
}
else if(uri.startsWith("/system/login")) {
//登陆接口设置⽩白名单
filterChain.doFilter(request, response);
}
else if(uri.endsWith(".do")||uri.endsWith(".action")){
//检测当前用户是否登陆
User user =(User) request.getSession().getAttribute("user");
if(user==null|| "".equals(user)) {
errorResponse(response, paramN, "未授权访问");
return;
}
}

URL中有一个保留字符分号;,主要作为参数分隔符进行使用,有时候是请求中传递的参数太多了,所以使用分号;将参数对(key=value)连接起来作为一个请求参数进⾏传递。

直接在URI中引入分隔符,正常来说是不会对实际接口的访问造成影响的。

对于request.getRequestURL()request.getRequestURI()来说,使用&连接的参数键值对,其是获取不到的,但是参数分隔符;及内容是可以获取到的。

1
/system/UserInfoSearch.do;tkswifty

此时request.getRequestURL()方法得到的后缀不是.do,而是;tkswifty,从而认为该接口为非业务接口,绕过权限控制。

URL编码绕过

例如/system/UserInfoSearch.do接⼝是管理员才能访问的接口,需要进⾏⽤户检查,防止越权访问。

1
2
3
4
5
6
7
8
9
10
11
12
if(uri.equals("/system/UserInfoSearch.do")){
User user =(User) request.getSession().getAttribute("user");
String role = user.getRole();
if(role.equals("admin")) {
//当前⽤用户为admin,允许访问该接⼝
filterChain.doFilter(request, response);
}
else {
errorResponse(response, paramN, "越权访问");
return;
}
}

若不是admin用户登陆,拒绝访问UserInfoSearch.do接口。

当filter处理完相关的流程后,中间件会对请求的URL进行一次URL解码操作,然后再找到对应的Servlet进行访问。也就是说尝试对我们访问的URL进行一次URL编码,中间件是可以正常解析并完成正常业务的。

这里存在一个问题,使用request.getRequestURL()request.getRequestURI()这两个⽅法进⾏行行访问接口的获取时,是不会进行URL解码操作的,也就是说刚刚我们访问的。

我们可以通过对URI进行URL编码,此时filter中得到的uri并不是正常的/system/UserInfoSearch.do,⽽是编码后的,但是filter转发请求后浏览器可以解码并正常解析,从而达到以低权限用户绕过权限控制访问管理员接口的效果。