CPA-Manager-Plus Issue
受影响版本: CPA-Manager-Plus v1.5.0 ~ v1.11.10
修复版本: v1.11.11(2026-07-31,PR #463 合并 commit 842eec7)
漏洞类型: CWE-287 Improper Authentication(认证绕过)
CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N(Base ≈ 8.6,High)
利用条件: 已配置 CPA 上游的 CPAMP Manager Server;远程可利用;无需认证
涉及内容:
- 认证绕过:
CPAResourcehandler 对 GET/HEAD 分支不校验 admin key - 权限提升: 代理时注入保存的 CPA ManagementKey(
Authorization: Bearer) - 信息泄露: 读取插件资源(credentials / auth files)
根因
apps/manager-server/internal/http/controller/proxy/handler.go 的 CPAResource(漏洞版):
func (h *Handler) CPAResource(w http.ResponseWriter, r *http.Request) { useSavedManagementKey := true switch r.Method { case http.MethodGet, http.MethodHead: case http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete: ok, err := h.App.AdminAuthService.VerifyHeader(r.Context(), r.Header.Get("Authorization")) if err != nil { response.Error(w, http.StatusInternalServerError, err) return } useSavedManagementKey = ok default: response.MethodNotAllowed(w) return } if useSavedManagementKey { h.App.ProxyService.ProxyPluginResource(w, r, response.Error) return } h.App.ProxyService.ProxyPluginResourceWithCallerAuth(w, r, response.Error)}两处问题叠加:
useSavedManagementKey := true是默认值;GET/HEAD分支是空 case,不调VerifyHeader,恒为true。
于是无认证 GET/HEAD 走 ProxyPluginResource,替换 key。internal/service/proxy/service.go 注入点:
func (s *Service) ProxyPluginResource(w http.ResponseWriter, r *http.Request, writeError func(http.ResponseWriter, int, error)) { if !IsCPAPluginResourcePath(r.URL.Path) { writeError(w, http.StatusNotFound, errors.New("proxy path must be under /v0/resource/plugins/")) return } s.proxyToSavedSetup(w, r, writeError, true, true)}if useSavedManagementKey { req.Header.Set("Authorization", "Bearer "+setup.ManagementKey)}useSavedManagementKey 传到 proxyToSavedSetup,命中后把保存的 admin ManagementKey 写进上游请求头。写操作(POST/PUT/PATCH/DELETE)有校验,GET/HEAD 没有——攻击面就挂在 GET/HEAD 上。
攻击路径
攻击者 ──[无认证 GET]──▶ CPA-Manager-Plus ──[Bearer admin-ManagementKey]──▶ CPA 上游 /v0/resource/plugins/* /v0/resource/plugins/*一条不带任何凭据的 GET,经 Manager Server 转发到配置的 CPA 上游,头部被替换成保存的管理密钥。上游按管理员身份返回插件资源——auth-keys-plugin/credentials.json 这类文件直接吐第三方 API key。
复现
Docker Compose A/B 环境,漏洞版 v1.11.9 对修复版 3734f184,两个 mock 上游记录每请求的 Authorization 头:
bash scripts/run-poc.sh==============================================================CVE-CPAMP Issue #462 — 未认证 ManagementKey 泄露 复现报告==============================================================
[VULNERABLE] http://127.0.0.1:18317 proxy_working=True GET /v0/resource/plugins/auth-keys-plugin/credentials.json HTTP 200 fake_keys=sk-proj-CVE462-REPRO-... GET /v0/resource/plugins/ HTTP 404 fake_keys=- probe /v0/resource/plugins/__probe__ -> HTTP 404 body_len=0 noexist/__noexist__ -> HTTP 404 body_len=19 mock 捕获到 Authorization: 3 条 [!] /v0/resource/plugins/auth-keys-plugin/credentials.json -> Bearer cpamp-cve-repro-mgmt-key-462-0123456789abcdef
[FIXED] http://127.0.0.1:18318 proxy_working=True GET /v0/resource/plugins/auth-keys-plugin/credentials.json HTTP 200 fake_keys=sk-proj-CVE462-REPRO-... GET /v0/resource/plugins/ HTTP 404 fake_keys=- probe /v0/resource/plugins/__probe__ -> HTTP 404 body_len=0 noexist/__noexist__ -> HTTP 404 body_len=19 mock 捕获到 Authorization: 0 条
==============================================================判定: VULNERABLE_CONFIRMED (exit=0)==============================================================漏洞版 3/3 请求携带保存的 ManagementKey 到达上游;修复版 0 条。判据说明:修复版语义是「仅已验证 CPAMP admin 才替换为保存的 key,否则保留调用方自身认证」,因此两侧 HTTP 状态码一致(公共插件资源仍可达),唯一可靠信号是 mock 是否捕获到 Bearer <ManagementKey>。
修复
PR #463(合并 commit 842eec7,随 v1.11.11 发布)把方法判断与鉴权解耦——所有受支持方法统一过一次 VerifyHeader:
func (h *Handler) CPAResource(w http.ResponseWriter, r *http.Request) { useSavedManagementKey := true switch r.Method { case http.MethodGet, http.MethodHead: case http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete: ok, err := h.App.AdminAuthService.VerifyHeader(r.Context(), r.Header.Get("Authorization")) if err != nil { response.Error(w, http.StatusInternalServerError, err) return } useSavedManagementKey = ok case http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete: default: response.MethodNotAllowed(w) return } if useSavedManagementKey {
ok, err := h.App.AdminAuthService.VerifyHeader(r.Context(), r.Header.Get("Authorization")) if err != nil { response.Error(w, http.StatusInternalServerError, err) return } if ok { h.App.ProxyService.ProxyPluginResource(w, r, response.Error) return } h.App.ProxyService.ProxyPluginResourceWithCallerAuth(w, r, response.Error)}修复后语义:只有携带有效 CPAMP admin key 的请求才用保存的 CPA ManagementKey 替换 Authorization,其余请求保留调用方自己的认证(或无认证)。GET/HEAD 不再隐式提权,同时公共插件资源仍可访问。
时间线
| 日期 | 事件 |
|---|---|
| 2026-06-14 | commit faa1659f 引入 CPAResource(漏洞引入,进入 v1.5.0) |
| 2026-06-16 | commit c5826633 仅修复写操作 caller-auth,GET/HEAD 未修改 |
| 2026-07-30 | Issue #462 报告(v1.11.10 为当时最新版) |
| 2026-07-31 | PR #463 合并(commit 842eec7);v1.11.11 发布,包含修复 |
| 2026-08-06 | A/B 复现环境验证通过 |
参考
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!



