CVE-2026-92580:AVideo CloneSite 存储型命令注入

从密码配置到系统命令

AVideo 的 CloneSite 插件负责站点克隆,可通过 rsync 这一文件同步工具传输站点文件。CVE-2026-92580 的核心问题出现在该同步分支:处理函数把保存的 SSH 密码直接嵌入交给 shell(系统命令解释器)的命令文本,未正确隔离密码数据与命令语法。攻击者控制的密码因此可能成为服务器实际执行的命令。

这是存储型操作系统命令注入(CWE-78):恶意内容先作为插件配置写入数据库,之后在克隆客户端运行时才触发。保存配置本身并不等于立即执行命令;只要恶意值仍在,后续满足条件的克隆运行就可能再次触发。

披露的受影响范围为 AVideo ≤ 29.0,另列有 master 提交 8963b6a1。直接确认覆盖 29.0,范围内其他版本未逐一确认;目前可用的版本信息未列出已修复版本。

加密保存没有隔离 shell 语法

配置写入入口 objects/pluginAddDataObject.json.php 接收插件记录的 id,以及 object_data 中的配置 JSON。处理函数检查 User::isAdmin(),再通过 encryptIfNeed() 将配置保存到 plugins 表。密码字段 cloneSiteSSHPassword 使用加密值结构,但加密只改变存储形式:后续 decryptIfNeed() 解密后,攻击者提交的密码字符仍被保留。

克隆握手成功后,plugin/CloneSite/cloneClient.json.php 会对数据库文件名、文件目录、文件列表和克隆站点 URL 等字段应用字符白名单,只允许指定字符通过。在 rsync 分支中,SSH 用户名 cloneSiteSSHUser 和主机地址 cloneSiteSSHIP 也接受过滤。然而,密码没有经过 shell 转义,即没有被处理成只能作为参数数据解释的形式。

该文件第 265–269 行通过 str_replace(),把 cloneSiteSSHPassword->value 直接放进以单引号包围的密码参数。命令使用 sshpass 为 SSH 连接提供密码,并调用 rsync 同步文件,最后由 exec() 执行拼接后的命令文本。

单引号本应让 shell 将其中的内容视为字面值,但密码自身若包含单引号,就能提前结束这个边界,进而引入命令替换。命令替换是 shell 先执行一段命令、再把其输出放回原命令的机制。因此,注入命令会在 shell 解析阶段被求值,不以 sshpass 或 rsync 最终成功为前提。

CVE 记录将这个密码字段描述为 CVE-2026-41304 修复后遗留的注入点。其他字段已有过滤,并不能说明整个命令构造过程已经安全;此处失效的边界仍是密码从配置数据进入 shell 命令文本的位置。

到达漏洞分支需要哪些条件

攻击者不能仅靠提交一个密码字段就触发执行。CloneSite 必须启用,且 useRsync 必须为 true;停用插件时,这段插件代码不可达。在此基础上,还需要以下条件:

  1. 能够在管理员会话下保存配置。 请求通过 id 选中 CloneSite 插件,并在 object_data 中写入攻击者控制的密码。直接路径需要管理员会话;通过 CSRF 借用管理员会话的可能性另有部署限制。
  2. 克隆关系已经获准。 plugin/CloneSite/cloneServer.json.php 可登记待批准的克隆条目,plugin/CloneSite/changeStatus.json.php 用于激活。握手时,Clones::thisURLCanCloneMe() 检查请求站点的 URL 是否在 clone_SitesAllowed 中、状态是否为 a(已激活),以及密钥是否匹配。仅有待批准条目不足以通过握手。
  3. 克隆客户端随后实际运行。 触发方式可以是管理员通过 HTTP 请求 plugin/CloneSite/cloneClient.json.php,也可以是使用有效 myKey 的已配置命令行定时任务。客户端通过握手后,还需沿克隆流程完成数据库转储的下载和导入,才会到达 rsync 命令构造位置。

这些条件分别约束配置写入、克隆授权和执行时机。握手能够确认克隆关系,却不会消除密码中的 shell 语法;管理员身份检查也不能替代命令参数的安全处理。

CSRF 是有部署限制的投递途径

跨站请求伪造(CSRF,CWE-352)是指攻击者诱使已登录用户的浏览器向目标应用发出请求,并借用其会话执行操作。这里的 CSRF 作用是让管理员浏览器保存恶意插件配置,命令执行仍依赖后续克隆流程,不是一次跨站请求就必然完成的攻击。

配置写入接口虽然检查管理员身份,却没有 CSRF 令牌,也没有调用 forbidIfIsUntrustedRequest()。底层 Object::save() 原本通过 isUntrustedRequest() 检查请求是否来自同源,但 ignoreTableSecurityCheck() 豁免了 plugins 和 clone_SitesAllowed 两张表,因此这条数据库写入路径也没有受到该同源检查的保护。

公告描述的 CSRF 路径涉及与应用同机、负责处理 HTTPS 的反向代理,或同站点共置应用等部署条件,以及 HTTPS 会话 Cookie 的 SameSite=None 设置。该设置使 Cookie 不受 SameSite 的跨站发送限制;反向代理路径的解释还依赖 trustedProxies 未设置。不能仅凭接口缺少令牌,就断言任意部署都能由未登录攻击者完成投递。

这些条件还存在版本差异:29.0 设置了 Secure、SameSite=None 会话 Cookie,但没有公告所引用的回环地址信任逻辑及 trustedProxies 配置;该逻辑对应 master 提交 8963b6a1。公告描述的默认单进程 Apache 部署也不适用其 CSRF 路径。CSRF 投递尚未直接确认,已确认的执行路径使用管理员会话。

影响取决于触发进程,处置先阻断执行条件

已确认的管理员 HTTP 触发以 Web 进程用户 www-data 的权限执行命令,可读写该用户有权访问的文件,并创建可经 Web 访问的文件。已确认的影响发生在 Web 应用容器及其进程权限范围内,宿主机或独立数据库容器被攻陷并未得到确认。

定时运行会继承 cron 定时任务所属用户的权限。公告描述的部署中,该用户可能是 www-data,也可能是 root;如果任务以 root 权限运行,后果可能扩大到完全控制相应系统。不过,无人值守的定时触发和 root 身份执行均未直接确认,不能将其当作所有受影响部署的既定结果。

根据已知触发条件,停用 CloneSite 可使漏洞代码路径不可达;将 useRsync 设为 false 可阻断这里的 rsync 注入分支。这些属于从执行条件推导的临时规避措施,可能影响所需的克隆功能,不代表已有发布补丁。

阻断触发条件不会清除数据库中的恶意密码。恢复相关功能前,应检查 CloneSite 配置是否被篡改并移除恶意值,否则后续满足条件的运行仍可能重新执行注入命令。

参考资料