rConfig:经 Redis 作业队列的 RCE(CVE-2026-124079)
rConfig Core 8.0.0 至 8.2.18 和 Pro 8.4.1 之前的版本允许攻击者通过 Redis 作业队列执行代码,已在 8.2.19 和 8.4.1 中修复。
TL;DR
- rConfig Core 8.0.0 至 8.2.18 以及 Pro 8.4.1 之前的版本会反序列化 Redis 后端 Horizon 队列中的作业,因此特制作业能以工作进程身份执行代码。VulnCheck 于 2026-11-09 发布了 CVE-2026-124079。
- 直接攻击需要无需密码即可访问的 Redis。已登录用户也可以将设备指向 rConfig 服务器的回环地址,并写入同一队列,供应商公告描述了这一路径。
- VulnCheck 将其评为 CVSS v3.1 9.8 Critical 和 CVSS v4.0 9.3 Critical。目前没有已知利用,截至 2026-11-09 未列入 CISA KEV。
- 将 Core 升级到 8.2.19 或更高版本,将 Pro 升级到 8.4.1 或更高版本。升级后轮换已存储的密钥;"如何修复?"介绍了临时控制措施。
我受影响吗?
以下条件必须全部满足:
- 您运行 rConfig Core 8.0.0 至 8.2.18,或 rConfig Pro 8.4.1 之前的版本。
- 部署使用 Redis 队列驱动和 Laravel Horizon,这是这些版本的默认配置。
- Redis 可从不受信任的网络无密码访问,或者攻击者可以登录并添加或编辑设备。
供应商将 Core 8.2.18 列为受影响版本,尽管 8.2.18 为 TaskDownloadRun 作业添加了允许列表。该允许列表关闭了该作业中直接的 Artisan 参数注入。队列仍然没有身份验证,设备仍然可以指向服务器的回环地址。Core 8.2.19 是供应商列出的第一个修复版本。
| 分支 | 受影响版本 | 修复版本 |
|---|---|---|
| Core 8.x | 8.0.0 至 8.2.18 | 8.2.19(8.2.20 为当前版本) |
| Pro | 8.4.1 之前 | 8.4.1(8.4.2 即将发布) |
检查已安装的版本:
cat /var/www/html/rconfig/VERSION
# Docker:
docker exec rconfig_app cat /var/www/html/rconfig/VERSION
检查 Redis 是否要求密码。在 rConfig 主机上运行以下命令:
grep -E '^REDIS_(HOST|PORT|PASSWORD)=' /var/www/html/rconfig/.env
redis-cli -h 127.0.0.1 -p 6379 ping
PONG 表示 Redis 在没有密码的情况下接受了请求。NOAUTH 表示需要密码。如果响应为 PONG,且端口 6379 可从不受信任的网络访问,则直接路径处于开放状态。
查找指向服务器本身的设备:
mysql -u rconfig_user -p rconfig -e "
SELECT id, device_name, device_ip, device_port_override
FROM devices
WHERE device_ip IN ('127.0.0.1','localhost','::1','0.0.0.0')
OR device_ip LIKE '127.%'
OR device_ip LIKE '::ffff:127.%';"
如何修复?
将 Core 升级到 8.2.19 或更高版本。版本 8.2.20 是当前版本,还修复了配套的表导出凭据泄露问题 rCFG-SA-2026-10。Pro 用户应升级到 8.4.1 或更高版本;供应商表示 8.4.2 即将发布。
- 对于从 8.2.18 或更早版本升级的 Core 安装,请运行供应商的首次升级步骤:
cd /var/www/html/rconfig
git pull
sudo ./update.sh --no-pull
- 首次升级后,唯一的升级命令是:
sudo ./update.sh
- 对于 Docker,请在
.env中固定镜像版本,然后拉取并重新创建容器:
sed -i 's/^RCONFIG_VERSION=.*/RCONFIG_VERSION=8.2.20/' .env
docker compose pull
docker compose up -d
8.2.19 及更高版本的镜像在应用容器内运行 Redis,并在首次启动时生成其密码。
- 对于 Pro,请按照供应商的 Pro 升级说明升级到 8.4.1 或更高版本。
如果现在无法升级,请应用可在 8.2.18 上复现的两项控制措施。首先,为 Redis 设置密码。8.2.19 的命令是 php artisan rconfig:redis-secure,但该命令在 8.2.18 中不存在。在 8.2.18 上,请参考供应商的 RedisSecurityService 手动应用相同的控制措施:
SECRET=$(openssl rand -hex 32)
redis-cli -h 127.0.0.1 -p 6379 CONFIG SET requirepass "$SECRET"
redis-cli -h 127.0.0.1 -p 6379 -a "$SECRET" --no-auth-warning CONFIG REWRITE
sed -i "s/^REDIS_PASSWORD=.*/REDIS_PASSWORD=$SECRET/" /var/www/html/rconfig/.env
cd /var/www/html/rconfig
php artisan config:clear
php artisan horizon:terminate
horizon:terminate 信号会让 Supervisor 使用新密码重新启动 Horizon。如果您单独运行调度器,也请重新启动它。
其次,在升级之前将设备编辑限制为受信任的管理员。8.2.18 的界面仍然接受 127.0.0.1 或 localhost 的设备 IP,因此任何可以添加或编辑设备的账户都能使用回环路径。将 Redis 绑定到回环或专用网络,并用防火墙限制端口 6379。这会减少直接路径,但不会关闭回环路径。
升级后,轮换工作进程可以读取的密钥。供应商公告列出了 REST API 令牌、设备和 enable 密码,以及 SMTP、LDAP 和 RADIUS 密钥。检查用户列表,确认没有不认识的账户。
根本原因是什么?
队列通过 Laravel Horizon 由 Redis 提供后端,而 Redis 默认没有密码。Laravel 的 CallQueuedHandler 在没有类允许列表的情况下反序列化作业负载,因此指定了带有危险 handle() 方法的类的作业会执行该方法。随附的 TaskDownloadRun 作业就是这样的类之一:在 8.2.18 之前,它将其序列化的 task_command 和 id 字段直接传给 Artisan::call()。
在 8.2.18 标签中,config/database.php 第 121 至 126 行从 REDIS_PASSWORD 设置 Redis 密码,默认值为 null:
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
.env.example 文件提供 REDIS_PASSWORD=null。因此,默认安装会在没有身份验证的情况下连接 Redis。
在 8.2.17 标签中,app/Jobs/TaskDownloadRun.php 第 32 行将序列化的任务字段传给 Artisan:
Artisan::call($this->task['task_command'] . ' ' . $this->task['id']);
作业属性随队列负载一起传输。能够写入队列的攻击者控制 task_command 和 id,因此工作进程会执行任意 Artisan 命令,包括 tinker。8.2.18 的变更日志用其自己的话说记录了这一修复。它指出 TaskDownloadRun 将命令和 ID 从其自身的序列化属性直接传给 Artisan::call(),因此特制的队列负载可以执行任意 Artisan 命令。
版本 8.2.18 在 app/Jobs/TaskDownloadRun.php 第 58 至 78 行添加了允许列表:
$command = is_string($this->task['task_command'] ?? null) ? $this->task['task_command'] : '';
if (! in_array($command, self::ALLOWED_TASK_COMMANDS, true)) {
Log::warning('TaskDownloadRun rejected an unexpected task command.', [
'task_command' => $command,
]);
return;
}
if (! is_numeric($this->task['id'] ?? null)) {
Log::warning('TaskDownloadRun rejected a non-numeric task id.', [
'id' => $this->task['id'] ?? null,
]);
return;
}
$taskId = (int) $this->task['id'];
Artisan::call(self::RUN_COMMAND, ['taskid' => [$taskId]]);
该允许列表关闭了此作业中直接的 Artisan 参数注入。它没有关闭队列本身,也没有阻止设备指向服务器。供应商将 8.2.18 列为受影响版本,并在 8.2.19 中修复了其余路径。
第二条路径是设备目标。在 8.2.19 标签中,app/Rules/DeviceIpIsValid.php 第 29 至 36 行拒绝指向服务器的设备:
if ($this->pointsAtThisServer($value)) {
$fail('The :attribute cannot point at this rConfig server (loopback, localhost or its Redis host).');
}
/**
* A device is something rConfig connects out to from its own server, so a device on
* loopback lets device editors make rConfig write into its own Redis. See RCO-1813.
*/
该规则在 app/Http/Requests/StoreDeviceRequest.php 第 56 和 71 行应用于设备创建和更新时的 device_ip。Telnet 连接使用 fsockopen($this->hostname, $this->port) 打开原始 TCP 套接字,Send::sendString() 向其写入 $command . "\r\n"。因此,位于 127.0.0.1 端口 6379 的设备会将设备下载变成向 Redis 写入的原始 TCP 写入器。
供应商的修复代码在 app/Services/Utilities/RedisSecurityService.php 中说明了风险。它指出 Core 运行会反序列化队列负载的 Horizon 工作进程,因此任何本地进程都能写入的未认证 Redis 就是一条代码执行路径。
在原生安装中影响更大,因为队列工作进程以 root 运行。在 8.2.18 标签中,horizon_supervisor.ini 设置 user=root:
[program:horizon]
process_name=%(program_name)s
command=php /var/www/html/rconfig/artisan horizon
autostart=true
autorestart=true
user=root
8.2.19 版本将其改为 user=RCONFIG_WEB_USER,因此未来的队列缺陷不再以完整系统权限运行。
如何被利用?
- 访问队列。如果 Redis 无需密码即可访问,攻击者直接连接端口 6379。
- 如果 Redis 仅在回环上,则使用设备路径。攻击者登录并创建或编辑设备,将
device_ip设置为127.0.0.1、localhost、::1或0.0.0.0,将device_port_override设置为6379。 - 写入作业。设备连接打开到目标的 TCP 套接字并写入命令字符串。指向 Redis 时,同一套接字接受将序列化 Laravel 作业推送到 Horizon 队列的 Redis 命令。
- 工作进程反序列化。Horizon 的
CallQueuedHandler反序列化负载并调用作业的handle()方法。负载可以指定TaskDownloadRun或另一个带有危险处理程序的类。 - 作业运行。在 8.2.18 之前的版本中,
TaskDownloadRun运行攻击者的 Artisan 命令。在 8.2.18 中,该作业的允许列表阻止直接命令,但队列仍会反序列化其他类,回环路径也仍然存在。随附的laravel/tinker包会将 Artisan 命令变成任意 PHP 和操作系统命令。 - 攻击者持久化。CVE 描述记录了
storage/app/public中的 Web shell,以及数据库凭据、已存储的设备凭据和 API 令牌。
截至 2026-11-09,公告和 CVE 记录均未链接公开的概念验证。这些步骤描述的是来源记录的机制,不是可用的漏洞利用。
攻击如何展开?
能够直接或通过回环设备访问 Redis 队列的攻击者写入一个序列化作业,Horizon 将其反序列化并以工作进程身份运行。
无密码检查"] B --> C["将序列化作业推送到
Horizon 队列"] C --> D["CallQueuedHandler
无类允许列表"] D --> E["TaskDownloadRun 或 tinker
运行 Artisan 命令"] E --> F["以工作进程身份
执行代码"] F --> G["Web shell 和已存储的
设备凭据"]
攻击者能做什么?
攻击者以工作进程身份执行代码。在使用 8.2.18 Supervisor 存根的原生安装中,该用户是 root。在 Docker 镜像中,工作进程以 www-data 运行。
影响条件如下:
- 直接访问 Redis 要求 Redis 端口可从攻击者的网络访问,并且没有密码。
- 回环路径要求已登录的 rConfig 账户能够创建或编辑设备。
- 队列工作进程必须运行 Horizon,这是受影响版本的默认配置。
通过代码执行,CVE 描述记录了以下能力:
- 向 Web 提供的
storage/app/public目录写入持久 Web shell。 - 读取数据库凭据和 API 令牌。
- 读取 rConfig 用于访问受管网络设备的已存储设备凭据。
目前没有已知的在野利用,截至 2026-11-09 该 CVE 未列入 CISA KEV。
为什么修复有效?
修复关闭了通往队列的两条路径,并降低了未来队列缺陷的影响。供应商公告将 Core 8.2.19 和 Pro 8.4.1 列为修复版本。
第一项更改拒绝指向服务器回环或 Redis 主机的设备目标。8.2.19 的差异在现有 DeviceIpIsValid 规则中添加了 pointsAtThisServer():
@@ -22,7 +22,48 @@
// Check if it's a valid IP address or hostname/FQDN
if (! $this->isValidIpAddress($value) && ! $this->isValidHostname($value)) {
$fail('The :attribute must be a valid IP address or FQDN/Hostname.');
+
+ return;
+ }
+
+ if ($this->pointsAtThisServer($value)) {
+ $fail('The :attribute cannot point at this rConfig server (loopback, localhost or its Redis host).');
+ }
+ }
+
+ /**
+ * A device is something rConfig connects out to from its own server, so a device on
+ * loopback lets device editors make rConfig write into its own Redis. See RCO-1813.
+ */
+ private function pointsAtThisServer(string $value): bool
+ {
+ $host = strtolower(rtrim($value, '.'));
+
+ if ($host === 'localhost' || str_ends_with($host, '.localhost')) {
+ return true;
+ }
+
+ if ($host === strtolower((string) config('database.redis.default.host'))) {
+ return true;
+ }
+
+ $packed = @inet_pton($host);
+
+ if ($packed === false) {
+ return false;
+ }
+
+ // IPv4-mapped IPv6 (::ffff:127.0.0.1) reaches the same IPv4 address.
+ if (strlen($packed) === 16 && str_starts_with($packed, str_repeat("\0", 10) . "\xff\xff")) {
+ $packed = substr($packed, 12);
+ }
+
+ if (strlen($packed) === 4) {
+ // 127.0.0.0/8 is loopback, and Linux routes a connect to 0.0.0.0 to the local host.
+ return $packed[0] === "\x7f" || $packed === "\0\0\0\0";
+ }
+
+ return $packed === inet_pton('::1') || $packed === inet_pton('::');
}
该规则现在拒绝 localhost、配置的 Redis 主机、127.0.0.0/8 中的所有地址、0.0.0.0、::1、:: 以及 IPv4 映射的 IPv6 形式。已认证的回环路径被关闭。
第二项更改给 Redis 设置密码。新的 rconfig:redis-secure 命令调用 RedisSecurityService::secure(),生成 32 字节密钥,将 REDIS_PASSWORD 写入 .env,并在活动服务器上要求该密码:
$this->makeClient($current)->executeRaw(['CONFIG', 'SET', 'requirepass', $secret]);
然后运行 CONFIG REWRITE,使密码在重启后仍然有效,并重新启动队列消费者。安装程序和 Docker 入口点在全新安装时生成相同的密码。未认证的直接路径被关闭。
第三项更改以 Web 用户身份运行 Horizon 和调度器。8.2.19 的 horizon_supervisor.ini 将 user=root 替换为 user=RCONFIG_WEB_USER,发行说明指出这些服务不再以 root 运行。如果出现另一个队列缺陷,工作进程不再拥有完整系统权限。
供应商公告将 8.2.19 和 8.4.1 列为该 CVE 的修复版本。
如何检查是否已遭入侵?
CVE-2026-124079 没有公开的入侵指标。以下检查用于查找两条访问路径以及 CVE 描述记录的持久化。
检查 Redis 是否开放以及队列中有什么:
redis-cli -h 127.0.0.1 -p 6379 ping
redis-cli -h 127.0.0.1 -p 6379 --scan --pattern '*horizon*'
redis-cli -h 127.0.0.1 -p 6379 --scan --pattern '*queues*'
如果 Redis 要求密码,请为每条命令添加 -a "$REDIS_PASSWORD" --no-auth-warning。扫描会列出队列键。使用 lrange <key> 0 -1 检查返回的任何列表键。空队列是正常的。指定意外类的序列化负载是值得调查的线索。
查找指向服务器的设备:
mysql -u rconfig_user -p rconfig -e "
SELECT id, device_name, device_ip, device_port_override, created_at
FROM devices
WHERE device_ip IN ('127.0.0.1','localhost','::1','0.0.0.0')
OR device_ip LIKE '127.%'
OR device_ip LIKE '::ffff:127.%';"
在公共存储目录中查找 PHP Web shell:
find /var/www/html/rconfig/storage/app/public -type f -name '*.php' -printf '%TY-%Tm-%Td %TH:%TM %p\n'
grep -rIl '<?php' /var/www/html/rconfig/storage/app/public
合法的 rConfig 上传不需要在 storage/app/public 中放置 PHP 文件。将该处的任何 PHP 文件视为可疑,并与可信备份进行比较。
检查应用和队列日志中是否有被拒绝的作业或意外的 Artisan 命令:
grep -i 'TaskDownloadRun rejected' /var/www/html/rconfig/storage/logs/laravel.log
grep -iE 'tinker|artisan' /var/www/html/rconfig/storage/logs/laravel.log /var/www/html/rconfig/storage/logs/horizon.log
被拒绝的作业意味着有人发送了意外的命令。命中可能是失败的尝试,而不是入侵。没有公开指标,因此失败的尝试可能不会留下痕迹。
时间线
- 2026-10-08:rConfig Core 8.2.18 发布,在
TaskDownloadRun中添加允许列表,这是对 Artisan 参数注入的部分修复。 - 2026-10-27:rConfig Core 8.2.19 发布,包含 Redis 身份验证、非 root 队列工作进程和回环设备阻止。
- 2026-11-06:rConfig 发布针对该 CVE 的公告 rCFG-SA-2026-09,并发布 Core 8.2.20,同时修复表导出凭据泄露 rCFG-SA-2026-10。
- 2026-11-09:VulnCheck 发布 CVE-2026-124079,并将 komi Sergio Hognaglo(manasse)列为发现者。
从 8.2.18 部分修复到 CVE 发布之间的间隔为 32 天。
常见问题
CVE-2026-124079 是否已在野利用?
没有已知利用。供应商公告和 VulnCheck 公告均未报告攻击,截至 2026-11-09 该 CVE 未列入 CISA KEV。
该漏洞是否影响 rConfig Pro?
是。rConfig Pro 8.4.1 之前的版本受影响,供应商在 8.4.1 中修复了该问题。供应商表示 8.4.2 即将发布,包含更多修复。
Core 8.2.18 是否已修复?
否。Core 8.2.18 为 TaskDownloadRun 作业添加了允许列表,但供应商将 8.2.18 列为受影响版本。Core 8.2.19 是供应商列出的第一个修复版本。
如果 Redis 仅在回环上,我的实例是否暴露?
可能暴露。能够添加或编辑设备的已登录用户可以将该设备指向 127.0.0.1 的端口 6379,而 rConfig 会连接设备目标。如果设备编辑仅限于受信任的管理员,并且 Redis 有密码,暴露程度较低,但升级仍是修复措施。
应轮换哪些密钥?
轮换 REST API 令牌、已存储的设备和 enable 密码,以及 SMTP、LDAP 和 RADIUS 密钥。供应商公告还要求检查用户列表,确认没有不认识的账户。
防火墙能否替代升级?
不能。防火墙或将 Redis 仅绑定到回环可减少直接路径,但不会关闭设备回环路径。在升级之前,请应用 Redis 密码并限制设备编辑。
使用 Ostorlab 查找暴露的 rConfig 服务器
Ostorlab 的攻击面发现会清点面向互联网的主机,帮助您找到不应可访问的 rConfig 实例并优先修复它们。请从 ostorlab.co/product/attacksurface_discovery 开始。
参考文献
- VulnCheck,"rConfig Core 8.0.0 before 8.2.19 and rConfig Pro before 8.4.1 RCE via Unauthenticated Redis Queue Poisoning",https://www.vulncheck.com/advisories/rconfig-core-before-and-rconfig-pro-before-rce-via-unauthenticated-redis-queue-poisoning(2026 年 11 月 9 日检查)。
- CVE Program,"CVE-2026-124079",https://www.cve.org/CVERecord?id=CVE-2026-124079(2026 年 11 月 9 日检查)。
- rConfig,"Security Advisories & Vulnerability Disclosures",https://www.rconfig.com/security-advisories(2026 年 11 月 9 日检查)。
- rConfig,"rConfig V8 Core 8.2.19",https://github.com/rconfig/rconfig/releases/tag/core-8.2.19(2026 年 11 月 9 日检查)。
- rConfig,"rConfig V8 Core 8.2.20",https://github.com/rconfig/rconfig/releases/tag/core-8.2.20(2026 年 11 月 9 日检查)。
- rConfig,"CHANGELOG.md at core-8.2.19",https://github.com/rconfig/rconfig/blob/core-8.2.19/CHANGELOG.md(2026 年 11 月 9 日检查)。
- rConfig,"app/Jobs/TaskDownloadRun.php at core-8.2.17",https://github.com/rconfig/rconfig/blob/core-8.2.17/app/Jobs/TaskDownloadRun.php(2026 年 11 月 9 日检查)。
- rConfig,"app/Jobs/TaskDownloadRun.php at core-8.2.18",https://github.com/rconfig/rconfig/blob/core-8.2.18/app/Jobs/TaskDownloadRun.php(2026 年 11 月 9 日检查)。
- rConfig,"app/Rules/DeviceIpIsValid.php at core-8.2.19",https://github.com/rconfig/rconfig/blob/core-8.2.19/app/Rules/DeviceIpIsValid.php(2026 年 11 月 9 日检查)。
- rConfig,"app/Http/Requests/StoreDeviceRequest.php at core-8.2.19",https://github.com/rconfig/rconfig/blob/core-8.2.19/app/Http/Requests/StoreDeviceRequest.php(2026 年 11 月 9 日检查)。
- rConfig,"config/database.php at core-8.2.18",https://github.com/rconfig/rconfig/blob/core-8.2.18/config/database.php(2026 年 11 月 9 日检查)。
- rConfig,"app/Services/Utilities/RedisSecurityService.php at core-8.2.19",https://github.com/rconfig/rconfig/blob/core-8.2.19/app/Services/Utilities/RedisSecurityService.php(2026 年 11 月 9 日检查)。
- rConfig,"horizon_supervisor.ini at core-8.2.18",https://github.com/rconfig/rconfig/blob/core-8.2.18/horizon_supervisor.ini(2026 年 11 月 9 日检查)。
- rConfig,"horizon_supervisor.ini at core-8.2.19",https://github.com/rconfig/rconfig/blob/core-8.2.19/horizon_supervisor.ini(2026 年 11 月 9 日检查)。
- rConfig,"app/Http/Controllers/Connections/Telnet/Connect.php at core-8.2.18",https://github.com/rconfig/rconfig/blob/core-8.2.18/app/Http/Controllers/Connections/Telnet/Connect.php(2026 年 11 月 9 日检查)。
- rConfig,"app/Http/Controllers/Connections/Telnet/Send.php at core-8.2.18",https://github.com/rconfig/rconfig/blob/core-8.2.18/app/Http/Controllers/Connections/Telnet/Send.php(2026 年 11 月 9 日检查)。
- CISA,"Known Exploited Vulnerabilities Catalog",https://www.cisa.gov/known-exploited-vulnerabilities-catalog(2026 年 11 月 9 日检查)。
- rConfig,"rconfig/rconfig on GitHub",https://github.com/rconfig/rconfig(2026 年 11 月 9 日检查)。
- Docker Hub,"rconfig/rconfig",https://hub.docker.com/r/rconfig/rconfig(2026 年 11 月 9 日检查)。