CVE-2026-74997|RoundcubeWebmail插件可实现服务器远程代码执行

admin 2026-09-15 04:46:20 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: RoundcubeWebmail的markasjunk插件存在CVE-2026-74997远程代码执行漏洞,属OS命令注入(CWE-78),CVSS评分8.8高危。根因是占位符顺序替换可嵌套破坏escapeshellarg转义,攻击者可通过构造邮件头或身份邮箱实现RCE。影响1.6.0至1.6.17及1.7.0至1.7.2版本,官方已发布1.6.18和1.7.3修复,改用strtr一次性替换杜绝二次扫描。建议立即升级并审计插件配置。 综合评分: 85 文章分类: 漏洞分析,代码审计


CVE-2026-74997|Roundcube Webmail插件可实现服务器远程代码执行

alicy alicy

信安百科

2026年9月14日 09:00 河北

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

免责声明:本文仅供网络安全研究与防御技术学习交流使用。本文漏洞技术细节均来源于公开可查证的安全研究资料(CVE 官方记录、厂商安全公告及公开补丁代码),不含任何未公开或自行构造的攻击细节。请勿将文中信息用于未经授权的测试行为,因不当使用造成的后果由行为人自行承担。

#

0x00、前言

Roundcube 是全球部署最广的开源 Webmail 系统之一,基于 PHP 与 IMAP 协议构建,被主机托管商、高校、企业和政府机构广泛用于自建邮件服务。

据 BleepingComputer 报道,仅 Shodan 可追踪的互联网可达实例就超过 4.6 万个。为兼顾功能扩展,Roundcube 采用插件化架构,markasjunk(标记垃圾邮件)便是其内置插件之一。

2026 年 8 月 9 日,Roundcube 官方发布 1.6.18 与 1.7.3 两个安全版本,一次性修复 11 个安全漏洞,其中最严重的,正是 markasjunk 插件 cmd_learn 驱动中的远程代码执行漏洞(RCE):用户在网页上一次普通的「标记垃圾邮件」操作,就可能让攻击者构造的命令在邮件服务器上执行。

0x01、漏洞描述

该漏洞类型为 OS Command Injection(CWE-78),CVSS 3.1 评分 8.8(高危),攻击路径为网络可达、低权限、无需用户交互。

markasjunk 插件允许用户将邮件标记为垃圾邮件或非垃圾邮件,其 cmd_learn 驱动负责把被标记的邮件交给后端反垃圾系统(如 sa-learn、DSPAM)学习。实现方式是:将管理员配置的命令模板中的占位符(%u、%i、%s、%h:xxx 等)逐一替换为运行时数据,再交由 shell_exec() 执行。

缺陷在于替换按固定顺序进行:先完成替换的取值若内部包含后续占位符的字样,就会在 escapeshellarg() 建立的单引号保护内被二次替换,破坏引号上下文,使攻击者构造的载荷落入无引号区域。

利用前提是实例启用了该插件并使用 cmd_learn 驱动;按官方 CVSS 向量(PR:L),攻击者需具备已认证的低权限账户,纯默认安装不受影响。

0x02、CVE 编号

经 cve.org 官方记录核实,本漏洞编号为 CVE-2026-74997,由 MITRE 作为 CNA 收录(2026 年 8 月 17 日发布),关键信息如下:

| 项目 | 内容 | | — | — | | CVE 编号 | CVE-2026-74997 | | GHSA 编号 | GHSA-q5rh-mmp4-ppgq | | 漏洞类型 | CWE-78(OS Command Injection) |

#

0x03、影响版本

根据 CVE 官方记录中的产品状态,受影响版本范围为:

| 分支 | 受影响版本 | 修复版本 | | — | — | — | | 1.6(LTS) | 1.6.0 至 1.6.17 | 1.6.18 | | 1.7 | 1.7.0 至 1.7.2 | 1.7.3 |

#

0x04、漏洞详情

4.1 漏洞代码

漏洞位于 plugins/markasjunk/drivers/cmd_learn.php 的 _do_salearn() 方法。以下为 Roundcube 1.6.17 版本的关键代码(节选,①②③④为笔者标注,便于对应下文分析):

// 文件:plugins/markasjunk/drivers/cmd_learn.php(Roundcube 1.6.17)
private function _do_salearn($uids, $spam, $src_mbox)
{
    $rcube   = rcube::get_instance();
    // 命令模板来自管理员配置
    $command = $rcube->config->get($spam ? 'markasjunk_spam_cmd' : 'markasjunk_ham_cmd');
    // ……(解析模板中的 %h:xxx 头部占位符)……

    // ① 占位符按固定顺序逐一替换,每个值都经过 escapeshellarg() 转义
    $command = str_replace('%u', escapeshellarg($_SESSION['username']), $command);
    $command = str_replace('%l', escapeshellarg($rcube->user->get_username('local')), $command);
    $command = str_replace('%d', escapeshellarg($rcube->user->get_username('domain')), $command);

    if (strpos($command, '%i') !== false) {
        $identity = $rcube->user->get_identity();
        // %i = 当前用户的默认身份邮箱(用户可在「设置 - 身份」中自行填写)
        $command  = str_replace('%i', escapeshellarg($identity['email']), $command);
    }

    foreach ($uids as $uid) {
        $tmp_command = $command;

        // ② %s = 被标记邮件的发件人地址(发件人可控)
        if (strpos($tmp_command, '%s') !== false) {
            $message     = new rcube_message($uid);
            $tmp_command = str_replace('%s', escapeshellarg($message->sender['mailto']), $tmp_command);
        }

        // ③ %h:xxx = 被标记邮件的指定头部字段值(邮件内容可控)
        // ……
        $tmp_command = str_replace('%h:' . $header, escapeshellarg($val), $tmp_command);
        // ……

        // ④ 拼接完成的命令直接交给 shell 执行
        $output = shell_exec($tmp_command);
    }
}

(代码较长,可左右滑动查看完整内容)

4.2 占位符与攻击者可控点

命令模板由管理员配置,占位符决定了哪些运行时数据会进入命令行。逐项审视各占位符的数据来源,即可看出注入点所在:

| 占位符 | 含义 | 取值来源 | 攻击者可控性 | | — | — | — | — | | %u | 当前登录用户名 | 会话中的用户名 | 弱(通常由管理员分配) | | %l / %d | 用户名的本地部分 / 域名部分 | 由 %u 派生 | 弱 | | %i | 用户默认身份邮箱 | 用户在「设置 – 身份」中填写 | 强(受 identities_level 配置约束) | | %s | 被标记邮件的发件人地址 | 被处理邮件的 From 头 | 强(外部投递邮件即可控制) | | %h:xxx | 被标记邮件的指定头部字段 | 被处理邮件的邮件头 | 强(外部投递邮件即可控制) | | %f | 邮件原文临时文件路径 | 服务端 tempnam() 生成 | 不可控 |

其中 %i 来自用户自己设置的「身份邮箱」,%s 与 %h:xxx 则完全来自被标记邮件本身的发件人地址与邮件头,外部投递一封邮件即可控制。换言之,只要命令模板同时使用了「较早替换的可控占位符」与「较晚替换的占位符」,嵌套替换就有发生的土壤。

4.3 攻击链与利用机理

步骤一(满足前置条件):目标实例启用 markasjunk 插件,学习驱动为 cmd_learn,且管理员配置的命令模板中包含多个占位符。典型场景是与 sa-learn、DSPAM 等反垃圾系统的集成,驱动代码中甚至保留了 DSPAM 的兼容逻辑。

步骤二(构造替换值):攻击者让某个「较早替换」的占位符取值中,包含「较晚替换」的占位符字样。例如将身份邮箱(%i)设为 attacker%h:[email protected],同时在待标记邮件的 x-sig 头部字段中放置载荷。

步骤三(嵌套替换破坏引号):escapeshellarg() 先把身份邮箱包进单引号;随后 %h:x-sig 的替换在引号「内部」再次发生,插入的转义片段会提前闭合并重新打开引号,使载荷落入无引号上下文。

步骤四(触发执行):已认证用户在 Web 界面将这封邮件标记为垃圾邮件(或移动到 Junk 文件夹),_do_salearn() 对每封邮件执行 shell_exec(),注入的命令以 Web 服务运行身份执行。

引号上下文被破坏的过程如下图所示(命令模板为示意,实际模板由管理员配置):

[模板]    dspam --user %i --signature=%h:x-sig

[步骤 1]  替换 %i(身份邮箱被 escapeshellarg() 包上单引号):
          dspam --user 'attacker%h:[email protected]' --signature=%h:x-sig

[步骤 2]  替换 %h:x-sig(邮件头字段值同样被包上单引号):
          dspam --user 'attacker'PAYLOAD'@evil.com' --signature='PAYLOAD'

[步骤 3]  shell 解析 'attacker'PAYLOAD'@evil.com' 时:
          'attacker'    → 普通字面量 attacker
          PAYLOAD       → 落入无引号上下文,$( )、;、| 等元字符生效
          '@evil.com'   → 普通字面量 @evil.com

另一种触达路径亦见诸媒体报道:攻击者无需自身账户,直接向受害者投递构造邮件,待受害者将其标记为垃圾邮件时即触发同一代码路径,该路径依赖受害者交互。官方 CVSS 向量(PR:L/UI:N)按「已认证用户自行触发」路径评定,这也是个别媒体「无需登录」的说法与官方评分存在出入的原因。

说明:CVE 官方对该漏洞的描述为「通过精心构造的占位符替换值实现远程代码执行」,与上述代码逻辑吻合。截至本文撰写时,官方与主流公开渠道尚未发布完整利用细节,以上攻击链为基于官方补丁差异与 1.6.17 公开源码的机理分析。

(图片来源于网络)

4.4 官方修复方案

官方修复(1.6 分支 commit b8f90e2,1.7 分支同步修复)重构了替换逻辑:先把全部占位符与转义后的取值收集进数组,最后用一次 strtr() 完成替换:

// 修复后(commit b8f90e2,节选):先收集,后一次性替换
$vars = [
    '%u' => escapeshellarg($rcube->get_user_name()),
    '%l' => escapeshellarg($rcube->user->get_username('local')),
    '%d' => escapeshellarg($rcube->user->get_username('domain')),
];

if (strpos($command, '%i') !== false) {
    $identity = $rcube->user->get_identity();
    $vars['%i'] = escapeshellarg($identity['email']);
}

foreach ($uids as $uid) {
    $replace = $vars;

    // %s、%h:xxx、%f 等同样写入 $replace 数组
    // ……
    $replace['%s'] = escapeshellarg($message->sender['mailto']);
    $replace['%h:' . $header] = escapeshellarg($val);
    $replace['%f'] = escapeshellarg($tmpfname);

    // strtr 单次遍历替换,不会重新扫描已替换的内容
    $tmp_command = strtr($command, $replace);
    $output      = shell_exec($tmp_command);
}

strtr() 对原字符串只做单次遍历,替换结果不会被重新扫描,「替换结果中再次包含占位符」的情况从机制上被杜绝;同时各占位符取值仍然全部经过 escapeshellarg() 转义,原有防线得以保留。

另一个细节是,修复同时把 %u 的取值来源从 $_SESSION[‘username’] 调整为 $rcube->get_user_name(),使命令构造与当前运行上下文保持一致。

0x05、个人观察与判断

设计缺陷点评:「逐个转义 + 顺序替换」是结构性缺陷:只要替换可以嵌套发生,任何单值转义都会被后续替换从内部破坏。2023 年的修复只补了 escapeshellarg 而未动替换逻辑,属于治标不治本;本次官方改用 strtr 一次性替换,才算触及根因。

行业趋势思考:Webmail 是 APT 组织的长期猎物。2026 年 2 月,CISA 已将 Roundcube 的 CVE-2025-49113(CVSS 9.9)等漏洞列入 KEV 已知被利用目录,Shadowserver 此前通报过约 8.4 万个暴露实例。非默认插件与可选驱动构成的「隐形攻击面」,正在成为最容易被资产清点遗漏的一环。

排查建议:立即检查配置中 markasjunk_learning_driver 是否为 cmd_learn;升级至 1.6.18 或 1.7.3;非必需场景直接禁用该插件;审计 markasjunk_spam_cmd 与 markasjunk_ham_cmd 的占位符组合;回溯排查 sa-learn、DSPAM 调用日志中的异常参数与服务器上的未知进程。

0x06、参考链接

https://cve.org/CVERecord?id=CVE-2026-74997

CVE 官方记录:漏洞描述、CVSS 向量与受影响版本范围

https://roundcube.net/news/2026/08/09/security-updates-1.6.18-and-1.7.3

Roundcube 官方安全公告:1.6.18 / 1.7.3 更新说明,共 11 项安全修复

https://github.com/roundcube/roundcubemail/commit/b8f90e28a46d42e79a69568cba897f8f4223d9cd

官方修复 Commit:以 strtr 单次替换重构占位符替换逻辑


— END —

❤ 如果觉得有帮助,点个红心支持一下吧。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:信安百科 alicy alicy《CVE-2026-74997|Roundcube Webmail插件可实现服务器远程代码执行》

    评论:0   参与:  0