dde-api-proxy:Deepin D-Bus代理服务中的身份验证绕过 (CVE-2025-23222)
#本地 #D-Bus #deepin #CVE目录
- 1) 引言
- 2) 身份验证绕过问题
- 3) 复现步骤
- 4) 受影响的 D-Bus 接口
- 5) 建议的错误修复
- 6) 上游错误修复
- 7) 可能的临时解决方案
- 8) CVE 分配
- 9) 时间线
- 10) 参考资料
1) 引言
我们收到了 Deepin 桌面环境一部分的 Deepin api-proxy D-Bus 服务的审查请求。在审查过程中,我们发现此 D-Bus 服务设计中存在一个严重的身份验证缺陷,允许本地用户以多种方式提升权限。
我们在十二月私下将此问题报告给了 Deepin 安全团队,但一个月未收到回复。在我们准备发布时,上游重新活跃起来并迅速发布了一个错误修复,但遗憾的是,该修复仍不完整。
本报告基于 dde-api-proxy 版本 1.0.17。这些发现仍然适用于 1.0.18 版本。上游已尝试在 1.0.19 版本中修复这些问题,但如第 6 节所述,修复效果不佳。
2) 身份验证绕过问题
Dde-api-proxy 以 root 用户身份运行,并在 D-Bus 系统总线上提供各种 D-Bus 服务。它之所以突出,是因为它提供了大量的 D-Bus 配置文件,但代码却很少。其原因是该服务仅在客户端和实际的 Deepin D-Bus 服务之间转发 D-Bus 请求。我们认为这是为了向后兼容,因为 Deepin D-Bus 接口名称发生了变化,尽管 该组件的 GitHub 存储库对其目的的洞察很少。
在启动期间,代理服务会主动注册请求的旧版 D-Bus 接口,并与实际的 Deepin D-Bus 服务建立连接,消息将转发到该服务。当客户端向其中一个旧版服务名称发送消息时,代理会同步地转发消息(参见 handleMessage())通过其现有连接,并将回复返回给客户端。
然而,这种相当直接的 D-Bus 消息代理方法存在一个严重的严重安全漏洞
- 代理以
root用户身份运行。 - 代理会将任意本地用户的消息转发到实际的 D-Bus 服务,而没有任何身份验证要求。
- 实际的 D-Bus 服务不知道代理情况,它们认为
root用户正在要求它们执行操作。
因此,借助 dde-api-proxy,通常非 root 用户无法访问的旧版 D-Bus 方法将变得可以访问,且无需身份验证。
3) 复现步骤
无 Polkit 的 D-Bus 方法
以下是基于 Deepin Grub2 服务的问题的简单演示。该服务仅检查 D-Bus 客户端的 UID 以进行特权操作的身份验证。在下面显示的第一个命令中,使用了实际的 D-Bus 服务名称,并且服务拒绝了该操作,因为调用者没有特权。
user$ gdbus call -y -d org.deepin.dde.Grub2 \
-o /org/deepin/dde/Grub2 -m org.deepin.dde.Grub2.SetTimeout 100
Error: GDBus.Error:org.deepin.dde.DBus.Error.Unnamed: not allow :1.167 call this method
在下一个命令中,使用了旧版 D-Bus 服务名称,这次目标服务执行了该操作,因为它认为请求源于特权的 UID 0 客户端(dde-api-proxy)。
user$ gdbus call -y -d com.deepin.daemon.Grub2 \
-o /com/deepin/daemon/Grub2 -m com.deepin.daemon.Grub2.SetTimeout 10
()
使用 Polkit 的 D-Bus 方法
在之前的示例中,Polkit 身份验证未被涉及。当 Polkit 身份验证被涉及时,调用者被视为“管理员”,从而导致类似的权限提升。我们使用 Polkit 在 Deepin accounts 服务中找到了一个合适的例子。该服务提供了大量的系统操作,其中包括将用户添加到组的可能性。它检查“org.deepin.dde.accounts.user-administration” Polkit 操作的授权,该操作默认情况下仅允许本地会话中的用户提供管理员凭据。
以下 gdbus 调用尝试将 UID 为 1000 的非特权用户添加到 root 组。该调用仅在此方式下运行,当它从(Deepin)图形会话中执行时才能成功。此处调用了实际的 accounts 服务接口,因此操作失败(需要输入 root 密码)。
user$ gdbus call -y -d org.deepin.dde.Accounts1 -o /org/deepin/dde/Accounts1/User1000 \
-m org.deepin.dde.Accounts1.User.AddGroup root
Error: GDBus.Error:org.deepin.dde.DBus.Error.Unnamed: Policykit authentication failed
切换到 dde-api-proxy 提供的旧版 accounts 服务接口时,操作成功,无需任何身份验证请求,因为 accounts 服务再次认为 root 用户(UID 0)是请求此操作的客户端。
user$ gdbus call -y -d com.deepin.daemon.Accounts -o /com/deepin/daemon/Accounts/User1000 \
-m com.deepin.daemon.Accounts.User.AddGroup root
()
4) 受影响的 D-Bus 接口
我们并未深入研究 dde-api-proxy 使未经身份验证的本地用户可用的所有特权 D-Bus 方法。在某些代理接口上,只允许调用特定的一组“过滤方法”。然而,其他接口对调用方法没有限制。初步看来,dde-api-proxy 提供的以下 D-Bus 接口似乎存在有趣的攻击面。
- Accounts 服务(无方法过滤列表)
- 网络代理设置(无方法过滤列表)
- PasswdConf1 WriteConfig 方法
- Lastore 服务(Apt 后端,无方法过滤列表)
- Lastore manager 安装包方法
5) 建议的错误修复
身份验证绕过深植于 dde-api-proxy 的设计中,因此修复它很困难。在以下子部分中,提出了解决此问题的可能方法。
a) 降低权限
代理可以暂时将特权降级到非特权调用者的凭据,创建一个新的 D-Bus 连接并消息转发到正确的服务。如果 D-Bus 服务正在使用 Polkit 进行身份验证,这仍然无法正常工作,因为 Polkit 会区分调用者是否处于活动会话中。然而,D-Bus 代理服务永远不会处于会话中。这意味着身份验证要求可能会比必要的更严格。至少这种方法比目前的情况更安全。
b) 重新实现身份验证检查
代理可以自行实现所有必要的身份验证检查。这将导致执行正确的身份验证,但会导致大量代码的重复。它还会带来代理服务和实际服务的身份验证要求可能不同步的危险。
c) 在受影响的服务中实现旧版接口
最后,可以完全放弃 dde-api-proxy,并将向后兼容性实现在每个受影响的服务中。当然,这比 dde-api-proxy 试图实现的目标更不通用。
6) 上游错误修复
在长时间的沉默后,上游出人意料地回复了我们的报告,并建立了一个短暂的禁运期,直到 1 月 17 日发布了一个错误修复。该错误修复已在 1 月 17 日发布的上游 提交 95b50dd 中,并已包含在 1.0.19 版本中。
为了修复错误,上游采取了我们第 5.b) 节概述的建议方向,即在代理服务中实现冗余的 Polkit 授权检查。现在源代码中维护了一个代理提供的敏感 D-Bus 方法列表。所有这些方法都受一个新引入的 Polkit 操作“org.deepin.dde.api.proxy”保护,该操作需要管理员身份验证。
但是,此错误修复引入了一个新问题。Polkit 授权检查的实现方式如下:
bool checkAuthorization(const QString &actionId, const QString &service,const QDBusConnection &connection) const
{
auto pid = connection.interface()->servicePid(service).value();
auto authority = PolkitQt1::Authority::instance();
auto result = authority->checkAuthorizationSync(actionId,
PolkitQt1::UnixProcessSubject(pid),
PolkitQt1::Authority::AllowUserInteraction);
/* snip */
}
此代码将客户端的进程 ID (PID) 转发给 Polkit 服务进行身份验证。这种使用 Polkit UnixProcessSubject 的方式已被弃用很长时间,因为它容易受到竞争条件的影响,从而可以绕过此类授权检查。此问题已在 2013 年由前 SUSE 安全工程师 Sebastian Krahmer 发现,并被分配了 CVE-2013-4288。
上游在发布前未与我们分享错误修复,因此我们未能阻止此不完整的错误修复。通过切换到 SystemBusName 主题进行身份验证,应该可以修正此不完整的错误修复。
即使进行了改进的修复,我们仍认为此方法并非理想,因为它需要上游维护所有代理的方法。如果以后添加新方法,安全问题可能会再次悄然出现。此外,新引入的 Polkit 操作使得代理服务透明度降低,并可能影响用户体验。对于各个 D-Bus 方法的身份验证要求不再有精细的控制,所有这些代理 D-Bus 方法的身份验证消息都是通用的,对最终用户无益。
7) 可能的临时解决方案
除了从系统中移除 dde-api-proxy 之外,我们看不到任何可行的临时解决方案。
8) CVE 分配
这一发现是 dde-api-proxy 中一个较大的设计问题,它允许本地 root(组)利用以及更多类似的攻击向量。我们决定请求 Mitre 分配一个 CVE,以提高社区对该问题的认识。Mitre 为跟踪此问题分配了 CVE-2025-23222。
从形式上看,还需要为第 6 节所述的不完整错误修复引入的新安全问题分配另一个 CVE,但我们目前未这样做。
9) 时间线
| 2024-12-18 | 我们通过电子邮件将问题报告给了 security@deepin.com,这在项目的联系页面上有记录。电子邮件被邮件服务器拒收。 |
| 2024-12-18 | 我们联系了 support@deepin.org,询问报告 Deepin 安全问题的正确方法。我们很快得到了回复,他们指向了他们的(公开)bug 跟踪器或 security@deepin.org。 |
| 2024-12-19 | 我们通过电子邮件将问题报告给了 security@deepin.org,并提议协同披露。这次电子邮件未被拒收。 |
| 2025-01-07 | 由于尚未收到 Deepin 安全团队的回复,我们又发了一封邮件,要求在 2025-01-12 之前得到初步回复,否则我们将公布信息。 |
| 2025-01-13 | 由于仍未收到回复,我们开始着手发布完整报告。我们向 Mitre 申请了 CVE。 |
| 2025-01-14 | Mitre 分配了 CVE-2025-23222。 |
| 2025-01-16 | 一位上游联系人出人意料地回复并确认了该问题,并表示他们正在处理错误修复。我们再次询问是否需要协同披露,并转达了已分配的 CVE。 |
| 2025-01-17 | 上游回复表示,他们希望将禁运期延长至 2025-01-20。 |
| 2025-01-23 | 由于在发布日期时上游没有任何活动,并且我们未收到通知,我们询问上游发布是否按计划进行。 |
| 2025-01-24 | 上游将错误修复指向了我们,该修复已于 2025-01-17 发布,没有进一步的沟通。 |
| 2025-01-24 | 由于错误修复已发布,我们决定公布所有信息。在审查错误修复时,我们发现它不完整,并通过电子邮件通知了上游。 |