Camaleon CMS授权不当漏洞CVE-2026-10715
Camaleon CMS 是一款基于 Ruby on Rails 的开源、免费、高级、动态内容管理系统,其定位为 Rails 生态下的 WordPress 替代方案。
一、基本情况
Camaleon CMS 是Rails生态最成熟的开源 CMS 之一,核心价值在于灵活的无代码内容建模 + Rails 原生安全性能 + 多站点 / 多语言”。
Camaleon CMS 主打 “适配项目而非项目适配”,适合 Rails 开发者、企业站、复杂内容结构站点,是WordPress在Rails世界强力替代。

栋科技漏洞库关注到 Camaleon CMS 2.9.2 版本的草稿自动保存接口存在授权不当漏洞,追踪为CVE-2026-10715,CVSS 4.0评分5.1。
二、漏洞分析
CVE-2026-10715是 Camaleon CMS 中的一个权限授权不当漏洞,攻击者篡改后的恶意草稿后续可能被合法编辑或管理员审核并发布。
漏洞控制器直接采信客户端可控post_id参数定位已有草稿,更新草稿前未校验当前用户是否拥有、创建该父文章,或是具备编辑权限。
已登录低权限用户可以向POST /admin/post_type/<POST_TYPE_ID>/drafts接口传入任意文章编号,覆盖其他用户文章对应草稿内容。
(一)根本成因
1、草稿相关路由在后台文章类型命名空间下对外开放(配置文件config/routes/admin.rb第17至33行):
resources :post_type, as: :post_type do
resources :drafts, controller: 'posts/drafts'
end
2、后台基础控制器仅校验用户是否完成登录认证(文件路径app/helpers/camaleon_cms/session_helper.rb,第153–155行):
def cama_authenticate(redirect_uri = nil)
params[:return_to] = redirect_uri
return if cama_sign_in?
因此草稿控制器在修改内容前必须执行资源级权限校验。
3、常规文章操作均做了权限控制,但草稿相关操作未添加校验
(文件路径:app/controllers/camaleon_cms/admin/posts_controller.rb,第74–132行):
def create
authorize! :create_post, @post_type
...
end
def update
...
authorize! :update, @post
end
CamaleonCms::Admin::Posts::DraftsController 的 create 与 update 方法未执行对等的 authorize! 权限校验。
4、漏洞所在的草稿查询逻辑直接信任由用户可控的父级文章ID
(文件路径:app/controllers/camaleon_cms/admin/posts/drafts_controller.rb,第11–23行):
if params[:post_id].present?
@post_draft = CamaleonCms::Post.drafts.where(post_parent: params[:post_id]).first
if @post_draft.present?
@post_draft.set_option('draft_status', @post_draft.status)
@post_draft.attributes = @post_data
end
end
...
if @post_draft.save(validate: false)
该查询没有限定归属范围至当前登录后台用户、指定栏目下文章,或是当前用户具备编辑权限的父文章。
5、服务端将攻击者传入的父级文章ID直接赋值给草稿数据
(文件路径:app/controllers/camaleon_cms/admin/posts/drafts_controller.rb,第54–67行):
post_data[:status] = 'draft_child'
post_data[:post_parent] = params[:post_id]
post_data[:user_id] = cama_current_user.id if post_data[:user_id].blank?
(二)已确认完整漏洞触发链路(源头至污点汇聚点)
污点源头:已登录攻击者可在草稿自动保存请求中控制 `post_id` 以及 `post[...]` 全部字段。
路由链路:POST /admin/post_type/<POST_TYPE_ID>/drafts`请求路由至CamaleonCms::Admin::Posts::DraftsController#create方法。
数据查询:执行 CamaleonCms::Post.drafts.where(post_parent: params[:post_id]).first,根据攻击者传入的父文章ID读取对应草稿。
数据篡改:@post_draft.attributes = @post_data使用攻击者可控数据覆盖草稿原有字段。
持久化存储:@post_draft.save(validate: false) 直接保存篡改后的草稿,未校验草稿归属权与编辑权限。
(三)权限绕过说明
系统预设权限规则定义于app/models/camaleon_cms/ability.rb第38–64行:
仅拥有栏目编辑权限的用户,只能修改自身创建的文章;如需编辑他人内容,必须额外具备 `edit_other` 权限。
草稿接口未调用authorize! :update, parent_post权限校验接口,查询草稿时未限定为当前用户可编辑的文章范围,绕过这套权限体系。
(三)相关代码位置
1、config/routes/admin.rb 17–33行:草稿相关路由定义
2、app/helpers/camaleon_cms/session_helper.rb 153–155行:仅做登录身份校验
3、app/controllers/camaleon_cms/admin/posts_controller.rb 74–132行:常规新建、更新流程携带权限校验
4、app/controllers/camaleon_cms/admin/posts/drafts_controller.rb 11–23行:存在漏洞的草稿查询与保存逻辑
5、app/controllers/camaleon_cms/admin/posts/drafts_controller.rb 54–67行:服务端接收草稿请求参数
6、app/models/camaleon_cms/ability.rb 38–64行:文章预设权限控制规则
7、app/assets/javascripts/camaleon_cms/admin/_post.js 21–31行:前端自动保存逻辑携带 post_id
8、app/views/camaleon_cms/admin/posts/form.html.erb 80–85行:前端初始化草稿ID与请求接口
三、POC概念验证
(一)前置条件
1、部署 Camaleon CMS 2.9.2 站点;
2、用户A拥有内容新建/编辑权限,创建一篇文章并生成配套草稿;
3、用户B已登录,权限等级更低,无权限编辑用户A的文章;
4、用户B持有后台有效会话Cookie与CSRF令牌。
(二)攻击步骤
步骤1:生成受害方草稿
使用用户A账号新建或编辑文章,由系统自动生成草稿,记录该文章的父ID:
USER_A_POST_ID=<victim-parent-post-id>
POST_TYPE_ID=<post-type-id>
步骤2:发送伪造的草稿自动保存请求
使用用户B账号,携带用户A的父文章ID向草稿接口发起请求:
管理员已设置登录后刷新可查看预期返回结果:
{
"draft": {
"id": <DRAFT_ID>
},
"_drafts_path": "/admin/post_type/<POST_TYPE_ID>/drafts/<DRAFT_ID>"
}
步骤3:验证越权篡改效果
重新登录用户A账号,查看原有文章绑定的草稿,标题、别名及正文内容已被用户B提交的数据覆盖。
预期现象:
正常情况下用户B无法通过文章控制器的更新接口(PostsController#update)修改用户A的文章;
但借助草稿控制器创建接口(DraftsController#create),填入用户A的文章ID后,用户B仍可覆盖其文章草稿。
(三)利用佐证
漏洞利用演示视频:
静态取证证据:

四、影响范围
Camaleon CMS 2.9.2
五、修复建议
Camaleon CMS>2.9.2
六、参考链接
管理员已设置登录后刷新可查看