在Linux服务Polkit中发布了7岁的特权升级错误

2021-06-15 23:07:40

Polkit是默认情况下安装的系统服务,在许多Linux发行版上安装。它由SystemD使用,因此使用SystemD的任何Linux发行版也使用Polkit。作为GitHub安全实验室的成员,我的工作是通过查找和报告漏洞来帮助提高开源软件的安全性。几周前,我发现了Polkit的特权升级脆弱性。我协调了Polkit维护者和Red Hat的安全团队的漏洞披露。它公开披露,修复已于6月3日,2021年6月3日发布,并分配了CVE-2021-3560。

该漏洞使未经特权的本地用户能够在系统上获取根外壳。使用少数标准命令行工具很容易利用,正如您在此短视频中看到的那样。在这个博客文章中,我将解释漏洞利用如何工作,并向您展示错误的源代码中的错误。

我发现的错误很旧。它在七年前推出了Commit BFA5036,首先用Polkit版本0.113发布。但是,许多最受欢迎的Linux发行版直到最近没有发货。

该错误在Debian及其衍生品(如ubuntu)上有一个略有不同的历史记录,因为Debian使用具有不同版本编号方案的Polkit叉。在Debian Fork中,Bug于Commit F81D021引入,并首次发布版本0.105-26。 Debian,Debian 10(“Buster”)最近稳定的发布,使用版本0.105-25,这意味着它并不脆弱。但是,一些Debian衍生品,如Ubuntu,基于Debian不稳定,这是脆弱的。

这是一张具有各种流行分布的表,以及它们是否易受攻击(请注意这不是一个全面的列表):

POLKIT是在引擎盖下运行的系统服务,当您看到如下所示的对话框时:

它基本上扮演法官的角色。如果您想做一些需要更高权限的东西 - 例如,创建一个新的用户帐户 - 那么它是Polkit的作业,以决定您是否允许这样做。对于某些请求,Polkit将使即时决定允许或拒绝,并且对于其他人来说,它将弹出一个对话框,以便通过输入密码来授予授权。

该对话框可能会使Polkit是一个图形系统的印象,但它实际上是一个后台过程。该对话框称为身份验证代理,它真的只是将密码发送给Polkit的机制。为了说明Polkit不仅适用于图形会话,请尝试在终端中运行此命令:

pkexec是sudo的类似命令,它使您可以像root一样运行命令。如果在图形会话中运行pkexec,则会弹出一个对话框,但如果在文本模式会话中运行它,例如ssh,则启动自己的文本模式身份验证代理:

$ pkexec reboot ====验证org.freedesktop.policykit.exec ===验证是否需要运行`/ usr / sbin / reboot'作为Super UserauthenticatingAs:Kevin Backhouse ,,,(kev)密码:

您可以使用从命令行触发Polkit的另一个命令是DBUS-SEND。它是发送主要用于测试的D-Bus消息的通用工具,但通常在使用D-Bus的系统上默认安装。它可用于模拟图形界面可能发送的D-Bus消息。例如,这是创建新用户的命令:

如果在图形会话中运行该命令,将弹出身份验证对话框,但如果在文本模式会话中运行它,例如SSH,则会立即失败。那是因为,与pkexec不同,dbus-send不会启动自己的身份验证代理。

脆弱性令人惊讶地易于利用。只需使用Bash,kill和dbus-send等标准工具,所有所需的是终端中的一些命令。

概念证明(PoC)Inspoit我在本节中介绍取决于已安装的两个软件包:AccountSservice和Gnome-Control-Center。在诸如Ubuntu桌面之类的图形系统上,默认情况下通常安装这两个包。但是,如果您使用的是非图形RHEL服务器,那么您可能需要安装它们,如下所示:

当然,该漏洞没有任何与AccountSservice或Gnome-Control-Center有关的东西。他们只是恰好是剥削的便利载体的光伏客户。 PoC取决于Gnome-Control-Center的原因,而不仅仅是SupplayService是微妙的 - 我会稍后解释一下。

要避免反复触发身份验证对话框(这可能很烦人),我建议您从SSH会话运行命令:

通过启动DBUS-SEND命令但在PORKIT仍处于处理请求的中间而终止时,触发该漏洞。我喜欢认为它在理论上是可以在正确的时刻粉碎Ctrl + C的理论上,但我从未成功过,所以我用少量的Bash脚本来这样做。首先,您需要测量运行DBUS-SEND命令的时间正常需要多长时间:

对我来说,这需要16毫秒,这意味着我需要在大约8毫秒后杀死dbus-send命令:

dbus-send --system --dest = org.freedesktop.accounts - type = method_call --print-reply / org / freedesktop /帐户org.freedesktop.accounts.createuser字符串:鲍里斯字符串:" Boris Ivanovich Grishenko& #34; INT32:1&睡眠0.008s;杀$!

您可能需要运行几次,并且您可能需要尝试延迟中毫秒的数量。当漏洞利用成功时,您将看到已创建名为Boris的新用户:

请注意,Boris是Sudo集团的成员,因此您已经很好地前往全方位升级。接下来,您需要为新帐户设置密码。 D-Bus接口期望散列密码,您可以使用openssl创建:

现在,您只需再次执行相同的技巧,除此之外,致电SetPassword D-Bus方法:

dbus-send --system --dest = org.freedesktop.accounts - type = method_call --print-reply / org / freedesktop /帐户/ user1002 org.freedesktop.accounts.user.setpassword字符串:' $ 5 $ fv2pqfurmmi879j7 $ alsj.w4ktp.mhrhxm2fyv3esipcf / qsfqulatmwuub'字符串:Goldeneye&睡眠0.008s;杀$!

同样,您可能需要尝试延迟的长度并在成功之前运行几次。此外,请注意,在此示例中,您需要粘贴在正确的用户标识符(UID)中,即“1002”,从openssl命令中加上密码哈希。

要帮助解释漏洞,这是DBUS-SEND命令期间涉及的五个主要进程的图:

虚线 - DBUS-SEND和身份验证代理上方的两个进程是非特权的用户进程。线以下的人是特权系统进程。在中心是DBUS-守护程序,它处理所有通信:其他四个过程通过发送D-Bus消息来彼此通信。

DBus-守护程序在Polkit的安全性中起着非常重要的作用,因为它使四个进程能够安全地沟通并检查彼此的凭据。例如,当认证代理向Polkit发送身份验证cookie时,它会通过将其发送到org.freedesktop.policykit1 d-bus地址来实现。由于该地址仅被允许通过根进程注册,因此没有虚拟化进程拦截消息的风险。 DBus-守护程序还分配了每个连接A“唯一总线名称:”通常是“:1.96”。除了不容易受到PID回收攻击的情况下,它有点像进程标识符(PID)。目前从64位范围内选择唯一的总线名称,因此没有被重用的名称引起的漏洞的风险。

帐户 - 守护程序从DBUS-SEND接收D-BUS消息。该消息包括发件人的唯一总线名称。让我们假设它是“:1.96”。此名称由dbus-emon附加到邮件中,无法伪造。

如果连接的UID:1.96是“0”,那么Polkit立即授权请求。否则,它会发送验证代理允许授权请求的管理员用户列表。

为什么杀死DBUS-SEND命令会导致身份验证旁路?该漏洞是上面列出的事件序列的步骤四。如果Polkit向DBus-守护程序询问连接的UID:1.96,但连接:1.96不再存在? DBUS-Daemon正确处理这种情况并返回错误。但事实证明,Polkit不会正确处理该错误。事实上,Polkit以特别是令人不幸的方式解释了错误:而不是拒绝请求,它处理请求,好像它来自一个使用UID 0的过程。换句话说,它立即授权该请求,因为它认为请求已提出要求从根过程中。

为什么漏洞的时间是非决定性的?事实证明,Polkit在不同的CodePath上向DID询问DBUS-守护程序的UID。大多数那些CodePaths正确地处理错误,但其中一个没有。如果早期杀死DBUS-SEND命令,它由其中一个正确的CodePath处理,请求被拒绝。要触发漏洞的Codepath,您必须在正确的时刻断开连接。并且因为有多个进程涉及,所以“正确时刻”的时间从一个运行到下一个时变化。这就是为什么它通常需要几点努力实现成功。我猜这也是先前未发现错误的原因。如果您可以立即杀死DBUS-SEND命令可以触发漏洞,那么我希望它会很久以前发现,因为这是测试的更明显的事情。

向请求连接的UID询问DBUS-守护程序的功能名为polkit_system_bus_name_get_creds_sync:

polkit_system_bus_name_get_creds_sync的行为是奇怪的,因为发生错误时,函数设置错误参数,但仍然返回true。当我写下我的错误报告时,这对我来说并不清楚,这是一个错误还是刻意的设计选择。 (事实证明这是一个错误,因为Polkit开发人员通过返回错误时,返回错误时,我的不确定性来自Polkit_system_bus_name_get_creds_sync的几乎所有呼叫者_creds_sync,而且也不只是检查布尔结果,还要检查在继续之前,错误值仍为null。漏洞的原因是在以下堆栈跟踪中未选中错误值:

0在polkitsystembusname.c的polkit_system_bus_name_get_creds_sync:3881在polkitsystembusname.c的polkit_system_bus_name_get_user_sync:5112在polkitbackendsessionmonitor-systemd.c的polkit_backend_session_monitor_get_user_for_subject:3033在polkitbackendinteractiveauthority.c的check_authorization_sync:11214在polkitbackendinteractiveauthority.c的check_authorization_sync:12275在polkitbackendinteractiveauthority.c的polkit_backend_interactive_authority_check_authorization: 9816在polkit_backend_authority_check_check_achorzization的polkitbackendauthority.c:2277在server_handle_check_achorization的polkitbackendauthority.c:7907在server_handle_method_call的polkitbackendauthority.c:1272

/ *每个主题都有一个用户;这由客户提供,因此我们依靠呼叫者验证其可接受性。 * / user_of_subject = polkit_backend_session_monitor_get_user_for_subject(priv-> session_monitor,主题,null,错误);如果(user_of_subject == null)转到; / *特殊情况:UID 0,root,是oxalways_授权用于任何* / if(polkit_is_unix_user( user_of_subject)&& polkit_unix_user_get_uid(polkit_unix_user(user_of_subject))== 0){结果= polkit_authorization_result_new(true,false,null);转到; }

我之前提到过我的PoC依赖于accountService的安装,而不是安装了Gnome-Control-Center。这是为什么? PoC不以任何可见的方式使用Gnome-Control-Center,我甚至没有意识到当我写下PoC时,我取决于它!事实上,我只发现了因为红帽安全团队无法重现我的poc。当我在RHEL 8.4 VM上尝试自己尝试它时,我也发现POC不起作用。这是令人费解的,因为它在Fedora 32和Centos Stream上工作得很好。事实证明,这是一个关键差异,是我的RHEL VM是一个没有安装GNOME的非图形服务器。那么为什么这么想?答案是policykit.imply注释。

一些Polkit操作基本上相同,因此如果已经授权,则默默授权对方有意义。 GNOME设置对话框是一个很好的例子:

在点击“解锁”按钮并输入密码后,您可以执行类似于添加新用户帐户的内容,而无需第二次验证。这是由policykit.imply Annotation处理,这些注释在此配置文件中定义:

换句话说,如果您被授权执行ControlCenter管理员操作,那么您还授权执行ContainService管理员操作。

当我在rhel VM上附加GDB到Polkit时,我发现我没有看到我之前列出的弱势堆栈跟踪。请注意,堆栈跟踪中的步骤四是从Check_Authorization_sync到自身的递归调用。在第1227行发生,这是Polkit检查PolicyKit.implate注释的地方:

polkitauthorizationResult * implied_result = null; polkitimplicitauthorization inclied_impliced_authorization; gerror * millized_error = null; const gchar * imply_action_id; imply_action_id = polkit_action_description_get_action_id(imply_ad); / * g_debug("%s由%s,checked" action_id, imply_action_id); * / millized_result = check_authorization_sync(权限,来电,主题,imply_action_id,详细信息,标志和amp; true_implicit_authorization,true,& millied_error);如果(moplied_result!= null){if(polkit_authorization_result_get_get_Is_Authorized(amplied_result)){g_debug(&#34 ;被授权(由%s)",imply_action_id);结果= illiDed_result; / *清理* / g_strfreev(令牌);转到; g_object_unref(implied_result); }如果(implied_error!= null)g_error_free(implied_error);

身份验证旁路取决于忽略错误值。它在1121号线忽略,但它仍然存储在错误参数中,因此您还需要被呼叫者忽略。上面的代码块具有名为Implied_Error的临时变量,当ImpliDed_Result而不是null时,它会被忽略。这是使旁路成为可能的关键步骤。

总结一致,身份验证旁路仅适用于其他Polkit操作所暗示的Polkit操作。这就是为什么如果安装了GNOME-Control-Center,我的Poc只能有效:它会添加策略kit.implate annotation,使我能够实现accountsservice。然而,这并不意味着Rhel是安全的这个脆弱性。漏洞的另一个攻击矢量是ProdanceKit,它默认在RHEL上安装,并且具有适当的PolybleKit.imply Annotation为包安装操作。 PackageKit用于安装软件包,因此可以利用它来安装GNOME-Control-Center,之后exproit的其余部分都如前所述。

CVE-2021-3560启用一个未特权的本地攻击者来获得root权限。它非常简单快捷,漏洞利用,因此您可以尽快更新Linux安装。安装了Polkit版本0.113(或更高版本)的任何系统都很脆弱。包括流行分布,如Rhel 8和Ubuntu 20.04。

如果您喜欢讨论安全漏洞(以及如何修复它们),请查看安全实验室团队正在在Twitter上执行或关注我们的其他一些工作。