第一章:私有Registry沦陷事件全景图谱
私有Docker Registry作为企业容器镜像分发的核心枢纽,其安全性直接关系到整个CI/CD链路与生产环境的可信基线。近年来,多起因配置疏漏、凭证泄露或未授权访问导致的Registry沦陷事件频发,攻击者借此植入后门镜像、窃取敏感构建上下文,甚至横向渗透至Kubernetes集群。 典型入侵路径包括:弱密码或默认凭据暴露于公网、未启用TLS导致Basic Auth凭证明文传输、未配置HTTP Basic Auth或OAuth2鉴权中间件、以及Registry后端存储(如S3、MinIO)权限过度开放。一旦失守,攻击者可执行任意镜像推送、覆盖基础镜像标签(如
alpine:latest)、或通过恶意
Dockerfile触发构建时代码执行。 以下为快速检测Registry暴露面的关键命令:
# 检查是否响应未认证请求(返回200即存在风险) curl -I http://registry.example.com/v2/ # 验证匿名用户能否列出仓库(正常应返回401) curl -X GET http://registry.example.com/v2/_catalog # 尝试获取镜像清单(若无需Token即可返回manifest,则严重告警) curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ http://registry.example.com/v2/myapp/manifests/latest
常见高危配置模式如下:
- 使用
registry:2镜像但未挂载自定义config.yml - 在
config.yml中遗漏auth配置段或错误设置token服务地址 - 将
http.addr绑定至0.0.0.0:5000且未前置反向代理做访问控制
下表对比了安全基线配置与典型误配项:
| 配置项 | 安全推荐值 | 高危误配示例 |
|---|
| 启用了TLS | true(强制HTTPS) | 仅启用HTTP,且未设http.tls |
| 身份验证 | 集成Keycloak/OIDC或Nginx JWT校验 | auth: {htpasswd: {realm: "Registry", path: "/auth/htpasswd"}}且htpasswd文件硬编码于镜像中 |
| 存储后端权限 | 最小权限策略(如AWS IAM Role限制仅s3:GetObject) | 使用Root Access Key,且Bucket Policy允许public-read-write |
graph LR A[外部扫描器探测80/443/5000端口] --> B{Registry响应v2 API?} B -->|是| C[尝试匿名/catalog列表] B -->|否| D[标记为低暴露面] C -->|成功返回仓库名| E[判定为未鉴权或弱鉴权] C -->|401/403| F[进一步测试Token服务连通性] E --> G[推送恶意busybox:backdoored镜像] F --> H[爆破htpasswd或窃取valid token]
第二章:未授权镜像推送的27类攻击路径深度解析
2.1 基于Docker CLI配置泄露的凭证盗用与越权推送
敏感配置文件暴露路径
Docker CLI 默认读取
~/.docker/config.json,其中可能包含 base64 编码的 registry 凭据:
{ "auths": { "https://index.docker.io/v1/": { "auth": "dXNlcjpwYXNzd29yZA==" // base64("user:password") } } }
该字段若被容器挂载或误提交至代码仓库,攻击者可直接解码获取凭据。
越权推送利用链
- 窃取
config.json后,执行docker login自动复用认证信息 - 通过
docker push向目标镜像仓库(如私有 Harbor)推送恶意镜像
权限影响范围对比
| 凭证类型 | 默认作用域 | 越权风险 |
|---|
| CLI config auth | 所有已登录 registry | 高(无命名空间隔离) |
| Registry token | 单次会话 | 中(需实时窃取) |
2.2 Registry v2 API未鉴权端点滥用与匿名写入链利用
关键未鉴权端点
Docker Registry v2 默认暴露的
/v2/健康检查与目录发现端点常被忽略鉴权配置:
GET /v2/ HTTP/1.1 Host: registry.example.com
响应 200 OK 表明服务可达;若
GET /v2/_catalog未设访问控制,攻击者可枚举全部仓库名。
匿名写入链构成
以下三步构成完整匿名写入链:
- 利用
POST /v2/<repo>/blobs/uploads/初始化上传(无需认证) - 通过
PUT /v2/<repo>/blobs/uploads/<uuid>?digest=sha256:...提交恶意镜像层 - 调用
PUT /v2/<repo>/manifests/<tag>绑定恶意层至公开标签
风险矩阵
| 端点 | 默认鉴权 | 匿名可操作 |
|---|
/v2/ | 否 | 探测、枚举 |
/v2/<repo>/blobs/uploads/ | 否(若未配readonly) | 上传任意 blob |
/v2/<repo>/manifests/<tag> | 否(若 registry 未启用 authz 插件) | 覆盖合法镜像 |
2.3 TLS证书绕过与中间人劫持导致的镜像篡改注入
常见证书绕过模式
开发中常因调试便利性禁用TLS验证,例如在Go客户端中:
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
该配置使客户端忽略服务器证书链校验,攻击者可在局域网内部署恶意代理,伪造镜像仓库响应并注入恶意层。
MITM注入路径对比
| 场景 | 影响范围 | 检测难度 |
|---|
| HTTP重定向劫持 | 全量拉取请求 | 低(明文可见) |
| TLS证书伪造 | HTTPS镜像源 | 高(需配合私钥或CA信任) |
防御建议
- 强制启用证书固定(Certificate Pinning)
- 使用可信CA签发的域名证书,禁用自签名通配符
2.4 Harbor/Portainer等UI组件CSRF+XSS组合漏洞触发的后台推送劫持
攻击链路解析
CSRF 诱使管理员提交恶意表单,配合 XSS 注入 `