返回

文章详情

通过 qvm-copy-to-vm 错误报告回传通道在 QubesOS 中执行任意代码

Hacker News2026年8月30日 08:51

我们已发布 Qubes 安全公告(QSB)第 118 号:在 qvm-copy-to-vm 错误报告中执行 Dom0 任意代码。此 QSB 的文本及其随附的加密签名如下所示,后面是对本公告的一般说明和验证指示。Qubes 安全公告 118 ---===[ Qubes 安全公告 118 ]===--- 2026-08-28 在 qvm-copy-to-vm 错误报告中执行 Dom0 任意代码 用户操作 ------------ 按照下面 "修补" 部分所述,继续正常更新 [1] 以接收安全更新。对此 QSB 无需其他用户操作。 概要 -------- 如果使用 `qvm-copy-to-vm` 从 dom0 复制文件到恶意 qube,则该 qube 可以将任意命令注入到 dom0。 影响 ------- 如果攻击者已经控制了一个 qube,并且用户从 dom0 向该受控 qube 发起 `qvm-copy-to-vm` 调用,则攻击者可以利用此漏洞将任意命令注入到 dom0,从而使攻击者能够控制 Qubes OS。 技术细节 ------------------ `qvm-copy-to-vm` 工具允许从 dom0 复制文件到指定的 qube。它使用 "qfile" 协议,该协议是一种简化的归档格式,包括简单的文件元数据(比 `tar` 或 `cpio` 要简单得多)。该协议还包括在传输结束时的确认,由目标发送回源。这一确认包括所有传输文件的校验和、错误代码(如果有的话)以及最后接收文件的名称。如果报告了错误,则 dom0 会显示一个 GUI 消息,包含错误信息和受影响文件的名称,由目标 qube 报告。该漏洞存在于对该文件名的处理过程中: 1. `wait_for_result()` 函数在将接收的名称传递给错误处理程序之前,调用 `sanitize_remote_filename()`: linux-utils/qrexec-lib/pack.c: 55 static void sanitize_remote_filename(char *untrusted_filename) 56 { 57 for (; *untrusted_filename; ++untrusted_filename) { 58 if (*untrusted_filename < ' ' || 59 *untrusted_filename > '~' || 60 *untrusted_filename == '"') 61 *untrusted_filename = '_'; 62 } 63 } 64 65 void wait_for_result(void) 66 { ... 98 /* 清理远程文件名 */ 99 sanitize_remote_filename(last_filename); 100 101 errno = hdr.error_code; 102 if (hdr.error_code != 0) { 103 switch (hdr.error_code) { 104 case EEXIST: 105 call_error_handler("在 QubesIncoming 目录中已经存在名为 '%s' 的文件", last_filename); 106 break; ... 2. 然后,`call_error_handler()` 调用 dom0 变体的错误报告函数 -- `gui_fatal()` -> `display_error()`,该函数使用 `system()` 来启动实际的错误对话框: core-admin-linux/file-copy-vm/qfile-dom0-agent.c: 15 void display_error(const char *fmt, va_list args) { 16 char *dialog_cmd; 17 char buf[1024]; 18 struct stat st_buf; 19 int ret; 20 21 (void) vsnprintf(buf, sizeof(buf), fmt, args); 22 ret = stat("/usr/bin/kdialog", &st_buf); 23 #define KDIALOG_CMD "kdialog --title '文件复制/移动错误' --sorry " 24 #define ZENITY_CMD "zenity --title '文件复制/移动错误' --warning --text " 25 if (asprintf(&dialog_cmd, "%s '%s: %s (错误类型: %s)'", 26 ret==0 ? KDIALOG_CMD : ZENITY_CMD, 27 program_invocation_short_name, buf, strerror(errno)) < 0) { 28 fprintf(stderr, "为错误消息分配内存失败 :( "); 29 return; 30 } 31 #undef KDIALOG_CMD 32 #undef ZENITY_CMD 33 fprintf(stderr, "%s ", buf); 34 system(dialog_cmd); 35 } 36 37 _Noreturn void gui_fatal(const char *fmt, ...) { 38 va_list args; 39 va_start(args, fmt); 40 display_error(fmt, args); 41 va_end(args); 42 exit(1); 43 } 问题是 `sanitize_remote_filename()` 仅移除了非 ASCII 字符(和双引号),但保留了 shell 元字符。然后,`system()` 运行构造的命令,包括通过 shell 控制的攻击者名称。请注意,VM 变体的 `qvm-copy-to-vm` 不受影响,因为其错误报告函数的版本不使用 `system()`: core-agent-linux/qubes-rpc/gui-fatal.c: 16 static void produce_message(const char *type, const char *fmt, va_list args) 17 { ... 31 if (progress_type && !strcmp(progress_type, "gui")) 32 { 33 switch (fork()) 34 { 35 case -1: 36 exit(1); // 还有什么 37 case 0: 38 if (geteuid() == 0) { 39 if (setuid(getuid()) != 0) { 40 perror("setuid 失败,没有调用 zenity/kdialog"); 41 exit(1); 42 } 43 } 44 fix_display(); 45 execlp("/usr/bin/zenity", "zenity", "--error", "--text", dialog_msg, NULL); 46 execlp("/usr/bin/kdialog", "kdialog", "--sorry", dialog_msg, NULL); 47 exit(1); 48 default:; 49 } 50 } 51 free(dialog_msg); 52 } 53 54 void gui_fatal(const char *fmt, ...) 55 { 56 va_list args; 57 va_start(args, fmt); 58 produce_message("致命错误", fmt, args); 59 va_end(args); 60 exit(1); 61 } 受影响的系统 ----------------- 所有 Qubes OS 版本均受影响。 修补 --------

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡