Java代码审计之垂直越权

  • A+

  垂直越权的业务场景主要是低权限的用户可以访问高权限用户的功能,或者是用户在没有通过认证授权的情况下能够直接访问需要通过认证才能调用的接口或者文本信息。

审计思路

  主要原因还是权限校验细粒度未覆盖接口,首先可以通过pom.xml、web.xml或者是相关注解进行一些权限控制措施的定位。例如通过web.xml找到权限过滤器,检查对应的安全控制措施,例如相关的接口进行访问前会对当前会话进行登陆检查等。然后结合实际业务检查是否存在遗漏或者缺陷,这里就是相关Filter、Interceptor以及类似Shiro等权限控制框架等措施的审计了。

  如果没有发现相关的权限校验机制或者覆盖面不全的话,那么很可能相关的业务接口就会存在垂直越权问题了。可以从服务端或者view层入手,常见的场景如下:

常见场景

仅对登陆态进行检查

  比较常见的权限控制措施是防止非登陆的用户进行业务操作,但是其权限细粒度仅仅覆盖登陆的会话,没有进一步进行细化,例如检查是否是管理员还是普通用户。从而导致越权漏洞的产生。

  例如如下接口,主要实现的是管理员通过id重置用户密码的业务,整个过程只需要传入需重置的用户id以及新密码,即可发送对应的邮件给相关用户并完成重置操作。

```java
/*
* 重置密码
*
* @param id
* @return
/
@RequestMapping("/resetpwd")
public String resetpwd(@RequestParam("id") Long id,@RequestParam("newPwd") String newPwd, HttpSession session) {
try {
SecurityLoginaccountDto loginAccount = loginaccountService.get(id);
newPasswordEmail(loginAccount.getLoginName(), loginAccount.getEmail(), newPwd);

        loginaccountService.resetpwd(id, CryptographyUtil.hashMd5Hex(newPwd));
        PageMessageUtil.saveSuccessMessage(session,"重置密码成功");
    } catch (Exception e) {
        PageMessageUtil.saveErrorMessage(session, "重置密码失败");
    }
    return "redirect:" + REQUEST_BASE_PATH + "list";
}

```

  此处存在相关的权限校验机制,通过拦截器的方式判断用户是否登陆:

java
public class LoginedInterceptor extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception{
HttpSession session = request.getSession();
Object loginuser = session.getAttribute(SystemBaseConstant.LOGIN_USER_ID);
if(loginuser==null||"".equals(loginuser)){
request.getRequestDispathcer("/login").forward(request,response);
return false;
}
}
return true;
}

  但是没有在接口处校验当前登陆的用户会话是否是管理员,那么就会导致对应的垂直越权缺陷。也就是说普通用户在登录后,直接访问如下接口,即可操作管理员的重置密码业务:

/resetpwd?id=1&newPwd=Passw0rd

仅在页面进行权限控制

  在实际进行业务开发的时候,因为考虑到权限划分的业务需求,对于已登陆的用户角色,需要根据不同的权限对view层进行保护,显示/隐藏Web应用程序的JSP/视图。例如操作用户的界面仅仅只能是管理员用户查看,普通用户的控制台是无法查看的等。那么同样的,如果相关的权限校验细粒度没有覆盖到接口,那么还是会存在越权问题。

  这里主要从view层入手,可以通过一些权限控制框架的注解进行定位,梳理相关页面中不同权限的业务接口,然后检查覆盖面是否合理。页面权限控制的实现可以通过框架进行实现,常见的有:

SpringSecurity

  主要通过引入spring security-taglibs标记库的依赖库:

xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.0.1.RELEASE</version>
</dependency>

  然后在jsp页面中引入标签库来实现:

xml
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

  常见的有:

| 标签 | 功能 |
| ---------------------------------- | ------------------------------------------------------------ |
| | 如果认证用户具有权限列表中的某一个权限,那么这个标签范围的内容将显示。 |
| | 如果当前用户满足特定权限,则显示标签范围的内容。 |

  举例说明:例如这里的delete操作页面只有管理员角色才能查看。

permission_Audit5.png

  那么就可以结合SpringSecurity的权限校验配置对/Manager/delete接口进行检查,例如相关的SpringSecurity权限控制仅仅考虑到了未授权访问的问题:

xml
<http user-expressions="false">
<intercept-url pattern"/system/**" access="isAuthenticated()"/>
<csrf/>
<logout/>
.....
</http>

  同时在/Manager/delete接口中也没有进行相应的权限判断:

java
@RequestMapping({"/delete"})
public String deleteById(Model model HttpServletRequest request){
String id =request.getPatameter("id");
boolean res=this.userService.delete(id);
.....
}

  也就是说我们使用低权限用户登录系统后,虽然看不到admin用户的页面,但是还是可以垂直越权调度其/Manager/delete业务接口的。

Shiro

  主要通过在jsp页面中引入标签库来实现:

xml
<%@tablib prefix="shiro" uri="http://shiro.apache.org/tags">

  常见的有:

| 标签 | 功能 |
| ------------------------------------------ | ------------------------------------------- |
| | 完成登陆认证后即可显示内容 |
| | 不在登陆状态可以显示内容 |
| | 拥有update权限资源的用户即可显示内容 |
| <shiro:lacksPermission name="update"& | 没有update权限资源的用户即可显示内容 |
| | 当前用户没有guest角色时即可显示内容 |
| | 当前用户拥有guest角色时即可显示内容 |
| | 当前用户没有guest或者test角色时即可显示内容 |

  举例说明:例如这里登录后必须是admin角色,才可以看到searchByName接口的相关页面:

permission_Audit4.png

  同样的可以结合shiro的权限校验配置对searchByName接口进行检查,如果权限控制的细粒度未覆盖到接口的话,那么说明存在垂直越权的缺陷。

自定义标签库

  除了使用框架自带标签库以外,还有些自定义的标签库,同样的也是根据对应的用户角色,在页面上进行权限划分的展示。

  举例说明:

  例如用户登陆成功后,系统会在当前用户会话session中对用户菜单进行预埋:

java
......
session.setAttribute(SystemBaseConstant.USER_MENU_LIST,permission.getMenuList(user.role))
......
return "Main";

  然后在对应的jsp页面进行相关权限页面的划分,首先从session会话中获取当前用户的用户菜单,然后结合dhtmlx进行自定义组件的生成,主要是在dhtmlToolbarCreator.validateButtonRight方法中进行实现:

xml
<% List<Object> menus=(List<Object>)request.getSession().getAttribute(SystemBaseConstant.USER_MENU_LIST);%>
......
toolbar = new dhtmlXToolbarObject("toolbarObj");
toolbar.addButton("query",1,"查询");
......
<%=dhtmlToolbarCreator.validateButtonRight(menus,4,"toolbar","query")%>
......
<script>
......
if(itemId=="query"){
query();
}
......
function query(){
......
$.ajax({
type:"POST",
url:"<%=SysInfo.BASE_ROOT>/UserQuery",
data:data;
dataType:"json",
error:function(XmlHttpRequest,textStatuc,errorThrown){
dhtmlx.alert(textStatus);
window.location+"<%=SysInfo.BASE_ROOT%>/login";
},
success:cutpageSet
});
......
}
</script>

  查看dhtmlToolbarCreator.validateButtonRight方法的内容,这里主要是根据当前会话中的menus中的菜单id与页面上对应功能的id进行比对,不一致则设置功能不可用:

java
public static String validateButtonRight(List<Object> menus,Integer buttonId,String toolbarname,String buttonName){
if(toolbarname==null||toolbarname.length()==0|| rights==null||buttonName==null||buttonName.length()==0){
return "";
}
if(!menu.contins(buttonId.soString())){
return toolbarname+".disableItem(""+buttonName+"")"
}else{
return "";
}
}

  再看到前面的jsp页面,其query方法是绑定了id为4的buttionId的,也就是说如果当前用户的menu中没有4这个值的话,在页面上是没有查询按钮的,也就没法使用/UserQuery接口:

xml
toolbar.addButton("query",1,"查询");
......
<%=dhtmlToolbarCreator.validateButtonRight(menus,4,"toolbar","query")%>
<script>
......
if(itemId=="query"){
query();
}
......
function query(){
......
$.ajax({
type:"POST",
url:"<%=SysInfo.BASE_ROOT>/UserQuery",
data:data;
dataType:"json",
error:function(XmlHttpRequest,textStatuc,errorThrown){
dhtmlx.alert(textStatus);
window.location+"<%=SysInfo.BASE_ROOT%>/login";
},
success:cutpageSet
});
......
}
</script>

  那么就可以对/UserQuery接口进行检查,可以看到权限控制的细粒度并未覆盖到接口,直接通过id即可进行查询,如果没有其他安全措施的话,基本存在垂直越权的缺陷。

java
@RequestMapping({"/UserQuery"})
public String UserQuery(String id){
User user = this.userService.query(id);
return user.toString();
}

认证缺失

  一些跨域的服务导向架构,尤其是一些数据处理的接口,如果缺少类似token的认证机制的话,也会存在类似的越权访问问题。

  例如需要向其他平台共享人员的数据,开发了相关的接口,但是缺少对应的认证凭证(token)检查,直接进行接口访问就可以未授权获取用户的敏感数据了。

总结

  关于垂直越权主要还是检查相关的安全控制措施,但是业务多、繁琐的情况下,通过页面的展示划分,还是能快速的进行接口定位,进行相关的安全审计的。

相关推荐: Spring 反序列化 RCE 漏洞分析

说在前面 原标题叫 《Spring framework 反序列化 RCE 漏洞分析》 奈何只能输入 26 个字符以下,故叫 《Spring 反序列化 RCE 漏洞分析》 相关分析漏洞是一个几年前的漏洞,并非是最近刚出的。之所以分析这个漏洞是因为笔者在梳理 fa…