Java代码审计之平行越权

  • A+

  平行越权的业务场景主要是Web 应用程序接收到用户请求,对某条数据进行业务操作时(例如编辑收货地址),没有判断数据的所属人,或判断数据所属人时,从用户提交的request参数(用户可控数据)中获取(不可信)。访问数据层(dao)的所有的业务操作,都可能产生这个漏洞。

审计思路

  主要是定位相关业务的关键凭证,然后再结合实际的业务场景进行判断是否存在平行越权的缺陷。

  常见的业务凭证有:

  • 用户身份id(userid,例如通过userid越权查看用户信息)
  • 对象id(fileid,例如通过fileid越权查看其他用户的备案资料(身份证、手机号、住址等))
  • 基于文件名(例如可以遍历时间戳组成的文件名获取用户订单)
  • Cookie中的字段
  • SessionID
  • ......

  除了可以随着业务接口挨个检查,还可以结合Entity进行快速定位。一般的Entity类对应一个数据表,其中的属性对应数据表中的字段。因为访问数据层(dao)的所有的业务操作,都可能产生平行越权缺陷,所以通过Entity可以快速的找到数据库中的关键凭证字段在service层实际业务的具体映射。

  举个例子:

  一个简单的User实体类:

```java
public class User {
private Long UserId;
private String username;
private String address;

@Override
public String toString() {
    return "User{" +
            "UserId=" + UserId +
            ", username='" + username + ''' +
            ", address='" + address + ''' +
            '}';
}

public Long getUserId() {
    return UserId;
}

public void setUserId(Long UserId) {
    this.UserId = UserId;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getAddress() {
    return address;
}

public void setAddress(String address) {
    this.address = address;
}

}
```

  这里UserId应该是数据库User表的主键,用户相关的业务很可能就与UserId有关,例如通过id查看用户信息,删除用户等。因为开发在编写代码时都是有自己的习惯的,这里可以尝试搜索UserId关键字,如果搜索结果中有对应的业务方法且UserId是其参数,那么基本上即可断定UserId就是业务关键凭证了,例如:

java
userService.query(UserId)

  这里通过UserId可以查询相关的用户信息,如果其不是从session会话中获取或者前端传递的话,那么很大可能会存在平行越权缺陷。同理,通过上述方法还可以筛选出其他需要UserId的业务接口,那么就可以快速进行检查了。

  除此之外,还可以直接查看service层方法的参数来进行关键凭证的定位,不过重复的太多,而且方法也多,效率上还是从Entity实体类直接梳理会比较快。

  定位到接口后需要检查以下几个方面

  • 业务凭证是否用户可控
  • 相关凭证是否可猜解
  • 多凭证场景下,是否绑定用户身份。例如fileid没有绑定用户身份,可遍历查看其他用户的备案资料(身份证、手机号、住址等)
  • 对应的方法中是否存在对应的权限注解

  例如通过Spring Security中的@PostAuthorize注解,在下例子中可以确保登录用户只能获取他自己的用户对象。防止平行越权的产生:

permission_Audit6.png

常见场景

  获取到业务凭证后,通过搜索可以快速定位相关的接口进行检查,常见的缺陷场景如下:

敏感身份凭证用户可控

  部分业务凭证是没必要前端传递的,例如通过userId查看个人信息,应该从session会话中获取,防止越权操作。例如如下案例:

  直接从前端获取userId,然后进行用户信息查询,如果service层也没有类似的权限注解防护的话,就可以平行越权查询其他用户的信息了:

java
@ResponseBody
@RequestMapping("/queryUserInfo")
public UserVO queryUserInfo(String userId){
UserVO user=userService.queryUserById(userId);
return user;
}

  当然也有可能该接口是后台管理员执行的,那么就需要开发文档或者跟开发人员进行二次确认了,然后就需要进行类似垂直越权、csrf等漏洞的检查了。

  正确的写法应该从会话中获取userId,避免用户可控:

java
UserVO user=userService.queryUserById(session.getAttributes("userId"));

关键业务凭证未与当前的用户身份进行绑定

  除了上面的情况以外,有时候还可能传递一些卡号、文件id等参数。这类的参数一般比较多,例如一个用户可能拥有多个文件id,基本不会在session容器中存放。那么在设计时如果没有跟当前的用户身份进行绑定的话也可能出现越权问题。

  例如下面的例子,直接通过传递卡号id进行卡号信息的获取:

java
@PostMapping("getUserCard")
public Object getUserCard(@RequestParam String cardId){
return userService.getUserCard(cardId);
}

  getUserCard方法的具体实现:

java
public Object getUserCard(String cardId){
if(StringUtils.isEmpty(cardId)){
return RespBean.success(null);
}
List<String> list = new ArrayList<>();
list.add(Constant.TOKEN_STATE_INITIALIZED);
list.add(Constant.TOKEN_STATE_ACTIVE);
CardInfo info = new CardInfo();
info.createCriteria().andCardIdEqualtp(cardId).andStatusIn(list);
return selectCard(info);
}

  由于未检查cardId是否是当前用户所属,存在越权查看其他用户卡号信息的风险。

  正确的做法应该是将cardId与当前用户身份进行绑定,在卡号查询前先检查当前卡号是否是用户所属:

java
User user = userService.getUserById(session.getAttributes("userId"));
if(cardId.equals(user.getCardId())){
return userService.getUserCard(cardId);
}

相关凭证可猜解

  还有就是可能对类似的凭证进行一定的处理,攻击者无法猜解其生成方式,或者无法解决sign签名认证,从而增加了攻击难度。

  主要检查对应的方式,常见的缺陷例如可以遍历时间戳组成的文件名获取用户订单,或者对应的id未使用UUID生成,仅仅是MD5对数字进行加密等。

其他

  同样的平行越权也会涉及到过滤器的问题,那么就是过滤器的审计了,例如审计获取接口的方式等。跟垂直越权是类似的。

相关推荐: Instagram应用代码执行漏洞详解(三)

在上一篇文章中,我们为读者介绍wildcopy型漏洞的利用方法等内容,在本文中,我们将继续为读者介绍Mozjpeg内部的内存管理器以及相关堆布局等知识。 Mozjpeg内部的内存管理器 现在,让我们回顾一下cinfo最重要的一个结构成员,具体如下所示: ```…