目录

引言

gnome-remote-desktop 通过 VNC 或 RDP (Microsoft 远程桌面) 网络协议提供对图形系统的访问。在 46 版本之前,gnome-remote-desktop 仅在现有图形用户会话的上下文中被使用。从 46 版本开始,还可以配置一个系统守护进程,允许连接到 GNOME 显示管理器 (GDM),从而允许远程创建图形会话。

系统守护进程以专用的“gnome-remote-desktop”用户身份运行。它在 D-Bus 系统总线上提供一个 D-Bus 接口。该守护进程还与 GDM 提供的新引入的 D-Bus 接口进行交互,以创建远程显示。

在审查新的系统服务时,我发现了一些本地安全问题和需要改进的安全方面。本报告讨论了更相关的问题,而上游的 Gitlab 问题 包含了一份更详细的报告和讨论,也涵盖了审查中发现的较不严重的问题。

本报告涉及 gnome-remote-desktop 46.0 版本。除 C) 项(尚无修复)外,所描述问题的错误修复可在 46.2 版本中找到。

审查动机和范围

D-Bus 系统服务在添加到 openSUSE 发行版及衍生产品之前,需要 SUSE 安全团队的审查。随着系统守护进程的添加,在将 gnome-remote-desktop 添加到 openSUSE Tumbleweed 以配合 GNOME 46 的大规模发布之前,对其进行审查变得有必要。

审查主要关注新引入的系统级 gnome-remote-desktop 守护进程。此外,还重点关注与 RDP 协议相关的代码路径,RDP 协议是默认协议,比 VNC 协议更受青睐。

由于 gnome-remote-desktop 的代码库相当庞大,我将审查重点放在 D-Bus 方法、Polkit 认证和部分网络处理的安全性上。我也未仔细查看 FreeRDP 库,gnome-remote-desktop 使用该库来处理大部分 RDP 协议。

A) 未经验证的交接 D-Bus 接口 (CVE-2024-5148)

只有“org.gnome.RemoteDesktop.Rdp.Server”D-Bus 接口受到 Polkit 的保护。在此接口上,所有方法都需要 auth_admin 授权。另外两个接口“Dispatcher”和“Handover”未进行授权,所有本地用户均可访问。这会导致一些本地安全问题,将在以下子章节中描述。

本地私钥泄露

系统守护进程将公开的 SSL 证书及其对应的私钥存储在“/var/lib/gnome-remote-desktop/.local/share/gnome-remote-desktop/certificates”中。对服务主目录“/var/lib/gnome-remote-desktop”的访问仅限于服务用户“gnome-remote-desktop”,权限模式为 0700。

然而,通过“org.gnome.RemoteDesktop.Rdp.Handover”D-Bus 接口,任何本地用户都可以截获 SSL 私钥。私钥从 StartHandover D-Bus 函数返回。当远程桌面客户端连接到系统守护进程时,存在一个相当长的时间窗口,在此期间任何本地用户(即使是 nobody)都可以调用创建会话对象上的此方法。这是一个实现此目的的示例调用

gdbus call -y -d org.gnome.RemoteDesktop -o /org/gnome/RemoteDesktop/Rdp/Handovers/sessionc11 \
    -m org.gnome.RemoteDesktop.Rdp.Handover.StartHandover someuser somepass

这里的用户名和密码参数并不重要,它们只会转发给连接的客户端。作为另一个影响,这样做还会导致拒绝服务,因为会阻止正确的连接交接。

本地攻击者不必等待有人连接到系统守护进程,他可以自己通过 localhost 连接,以达到相同的效果。但是,需要有效的 RDP 身份验证凭据才能进入交接阶段。

此问题的影响是本地信息泄露和本地拒绝服务。信息泄露意味着 RDP 连接在系统上的完整性和隐私受到损害。此 简单的 Python 脚本 可以重现此问题。

系统凭据泄露

如果 RDP 连接使用了共享系统凭据(请参阅结构成员 GrdRemoteClient.use_system_credentials),那么具有低权限的本地攻击者可以通过调用 Handover 接口未经验证的 GetSystemCredentials() D-Bus 方法,以类似私钥泄露的方式以明文获取这些凭据。

利用这些系统凭据,攻击者将能够通过 RDP 连接到显示管理器。这不应直接授予对会话的访问权限,因为仍然需要进行显示管理器级别的登录。一种例外情况是,如果启用了自动登录等功能(我不知道它们是否适用于远程连接)。

可以通过 TakeClient() 获取套接字连接

同样未经验证的 D-Bus 方法 Handover.TakeClient() 允许系统中的任何本地用户获取处于交接状态的 RDP 客户端的文件描述符。这可能允许本地用户对 RDP 连接执行拒绝服务攻击,或者设置一个经过精心构造的 RDP 会话。

通过此调用获取套接字仅在某些系统守护进程状态下有效,最显著的是,似乎需要执行 StartHandover() 才能成功。我并未完全调查确切的前提条件。

错误修复和影响范围

此 CVE 仅影响 gnome-remote-desktop 46.0 和 46.1 版本,因为系统守护进程仅在这两个版本中引入。错误修复从 46.2 版本开始可用,可以在 提交 9fbaae1a 中找到。

应用了错误修复后,只有为之创建了新会话的用户才能调用交接接口。根据上游的说法,这仍然意味着所有具有 RDP 访问权限的用户共享相同的私钥,这按照协议设计是合理的。

B) find_cr_lf() 存在一字节的越界读取

此函数处理不受信任的预身份验证 RDP 协议网络数据(路由令牌),并查找终止的 \r\n 序列。函数 for 循环中的大小计算是错误的:如果缓冲区的最后一个字节是 0x0D,那么逻辑将访问越界后的下一个字节。这个缓冲区不是以 null 结尾的。

在大多数情况下,影响应该可以忽略不计。这是我在向守护进程发送了一个精心构造的数据包后从 Valgrind 获得的输出

==31119== Invalid read of size 1
==31119==    at 0x15A1EF: UnknownInlinedFun (grd-rdp-routing-token.c:65)
==31119==    by 0x15A1EF: UnknownInlinedFun (grd-rdp-routing-token.c:159)
==31119==    by 0x15A1EF: UnknownInlinedFun (grd-rdp-routing-token.c:239)
==31119==    by 0x15A1EF: peek_routing_token_in_thread (grd-rdp-routing-token.c:281)
<snip>

错误修复

错误修复从 46.2 版本开始,可以在 提交 663ad63172 中找到。

C) grdctl 工具在命令行接受明文密码

用于系统和会话上下文 RDP 设置的基于文本的 grdctl 配置实用程序,在以下调用方式下接受明文密码

grdctl [--system] rdp set-credentials <username> <password>
grdctl [--system] vnc set-password <username> <password>

这意味着,当以这种方式配置时,明文密码将通过 /proc 文件系统泄露,并通过 ps 命令在进程任务列表中可见。因此,其他用户可以访问身份验证数据。

错误修复

上游拒绝为该问题分配 CVE。他们认为共享凭据的敏感性较低,并表示用户还有其他方式来设置凭据,这些方式不会向其他用户泄露信息(GNOME 控制中心、D-Bus API、直接写入凭据文件)。一个允许通过 stdin 读取密码的功能请求已被添加到现有的 Gitlab 问题中。

时间线

2024-04-19 我通过上游 Gitlab 的一个 私有问题 报告了这些问题以及其他建议和意见,并提供了协调披露。
2024-04-22 上游决定公开处理除未经验证的 Handover D-Bus 方法之外的所有发现。其余的私有问题没有建立正式的协调发布日期。
2024-04-26 我向 Mitre 请求了一个 CVE 来跟踪 A) 节中描述的未经验证的 Handover D-Bus 方法问题。
2024-05-13 在 Mitre 数周未分配 CVE 后,双方同意上游将改向 RedHat 请求 CVE。
2024-05-20 上游收到了 CVE-2024-5148,用于跟踪未经验证的 Handover D-Bus 方法问题。
2024-05-21 在询问了其余私有问题的预期发布时间后,上游决定立即发布。

参考文献