目录

1) 引言

pam-u2f 模块允许在 PAM 认证堆栈中使用 U2F(通用第二因素)设备,例如 YubiKeys。这些硬件令牌可以用作第二重身份验证因素,或允许无密码登录。

我们一直在检查 openSUSE 代码库中的所有 PAM 模块,查找不当的返回值。在此过程中,我们发现 pam-u2f 模块实现中对 PAM_IGNORE 返回值的不当使用可能允许绕过第二重身份验证或在未插入正确设备的情况下进行无密码登录。

本报告基于 pam-u2f 1.3.0 版本

2) 对 PAM_IGNORE 返回值的处理不当

PAM 模块基本上由一组钩子函数组成,这些函数根据活动的 PAM 堆栈配置由 libpam 调用。每个 PAM 模块函数都返回一个 int,其中包含 libpam 头文件中定义的 PAM_* 返回值之一。这些返回值对于 PAM 身份验证过程的结果至关重要,因为 libpam 根据在处理活动 PAM 堆栈配置的 auth 管理组中配置的模块时遇到的返回值来报告身份验证成功或失败。

pam-u2f 模块的主要业务逻辑在 pam_sm_authenticate() 函数中,其中包含多个会导致 PAM_IGNORE 返回值的情况。以下是可以导致这种情况发生的可能情况列表:

  • 如果在 gethostname() 中发生错误。
  • 如果在 strdup()calloc() 中发生各种内存分配错误。
  • 如果 resolve_authfile_path() 失败(如果 asprintf() 失败,则此函数会失败)。
  • 如果 pam_modutil_drop_priv()pam_modutil_regain_priv() 失败。

返回 PAM_IGNORE 向 libpam 表示 pam-u2f 模块不应对应用程序收到的返回值做出贡献。如果没有模块报告决定性返回值,则 libpam 默认会报告身份验证失败。但是,如果 auth 管理组中的任何其他模块返回 PAM_SUCCESS,并且没有模块标记错误条件,则身份验证的总体结果将是“成功”。本节的其余部分将探讨这种情况的确切发生方式。

pam-u2f 文档中,PAM 模块的两个主要用例被陈述为:

# as a second factor
auth required pam_u2f.so authfile=/etc/u2f_mappings cue

# for password-less authentication:
auth sufficient pam_u2f.so authfile=/etc/u2f_mappings cue pinverification=1

在“第二重身份验证”场景中,pam-u2f 返回 PAM_IGNORE 意味着无需实际提供第二重身份验证即可登录。第一个因素身份验证模块(通常是 pam_unix 之类的)将设置 PAM_SUCCESS 返回值,这将成为整体身份验证结果。

在“无密码”身份验证场景中,当 pam-u2f 仅用于身份验证时,返回 PAM_IGNORE 可能意味着无需任何身份验证即可成功登录。这种情况的前提是 auth 管理组中的另一个模块返回 PAM_SUCCESS。存在一些实用程序模块,它们实际上不进行身份验证,但执行辅助功能或强制执行策略。一个例子是 pam_faillock 模块,它可以添加到 auth 管理组以记录身份验证失败尝试,并在发生过多失败尝试时锁定帐户一段时间。此模块在“preauth”模式下运行时,如果尚未达到最大失败尝试次数,则返回 PAM_SUCCESS。在这种情况下,当 pam-u2f 返回 PAM_IGNORE 时,PAM_SUCCESS 将成为整体身份验证结果。

攻击者可以尝试引发一种导致 pam-u2f 返回 PAM_IGNORE 的情况,以实现其中一个结果。特别是,引发内存不足的情况似乎很常见——例如,如果本地攻击者已经拥有用户访问权限,并希望通过 sudosu 来提升特权。

3) 上游修复

我们建议上游将有问题的 PAM_IGNORE 返回值更改为标记身份验证失败的其他值,例如内存分配错误的 PAM_BUF_ERR 或其他关键错误的 PAM_ABORT。此外,我们建议统一受影响函数中的错误处理,因为在 retval 变量中使用了 不同风格的返回值PAM_* 常量与子函数返回的文字整数混合)。

上游按照这些思路实现了一个 bugfix,该 bugfix 在提交 a96ef17f74b8e4 中可用。这个 bugfix 可作为 1.3.1 版本的一部分。Yubico 还为该 CVE 提供了自己的 安全公告

4) 剩余对 PAM_IGNORE 的使用

PAM_IGNORE 仅应用于明确定义的情况,例如在缺少 PAM 模块的必要配置时。即使在这种情况下,理想情况下也应该通过将配置设置传递给模块的 PAM 配置行来显式选择此行为。

应用 bugfix 后,pam-u2f 中仍然存在两种情况。这两种情况会在用户未找到身份验证文件且将“nouserok”选项传递给 PAM 模块时触发。

5) 可能的临时解决方案

如果无法立即应用 bugfix,则可以通过修改 PAM 堆栈配置中的 pam_u2f 行来应用临时解决方案,如下所示:

auth       [success=ok default=bad]    pam_u2f.so [...]

这样,即使 pam_u2f.so 返回 PAM_IGNORE,libpam 也会将其视为错误的身份验证结果。

6) 时间线

2024-11-20 我们向 Yubico 安全部门报告了此问题,并提供了协调披露。
2024-11-22 Yubico 安全部门接受了协调披露,并表示他们正在开发修复程序。
2024-12-06 Yubico 安全部门通知我们,计划在一月初发布修复版本。
2024-12-12 Yubico 安全部门与我们分享了他们建议的 bugfix。我们进行了小幅改进建议。
2025-01-08 Yubico 安全部门告知我们发布日期为 2025-01-14。
2025-01-10 Yubico 安全部门与我们分享了 CVE 标识符及其正式安全公告。
2025-01-14 如计划所示,上游 bugfix 版本 1.3.1 已发布。

7) 参考