我是Ubuntu的粉丝,所以我想尽可能让它变得安全。最近,我花了相当多的时间寻找Ubuntu系统服务中的安全漏洞,这主要是一次沮丧的练习。我发现(并报告)了一些问题,但大多数都不严重。Ubuntu是开源的,这意味着很多人在我之前看过源代码,似乎所有容易的bug都已经被发现了。换句话说,我不希望这篇博文给你的印象是Ubuntu充满了琐碎的安全漏洞;到目前为止,这不是我的印象。
这篇博文是关于在Ubuntu上提升特权的一种令人惊讶的直截了当的方式。只需在终端中使用几个简单的命令,再点击几下鼠标,标准用户就可以为自己创建管理员帐户。我已经制作了一段简短的演示视频,以展示这是多么容易。
现代操作系统上的漏洞如此容易被利用,这是不寻常的。在某些情况下,我编写了数千行代码来利用漏洞。大多数现代攻击都涉及复杂的诡计,比如利用内存损坏漏洞伪造堆中的假对象,或者用精确到微秒的符号链接替换文件来利用TOCTOU漏洞。因此,如今很少能找到不需要编码技能就能利用的漏洞。我也认为这个漏洞很容易理解,即使你事先对Ubuntu的工作原理没有任何了解,也没有任何安全研究经验。
免责声明:对于利用此漏洞的人来说,他们需要访问系统的图形桌面会话,因此此问题仅影响桌面用户。
(如果因为名为.pam_Environment的文件已存在而无法执行此操作,则只需临时重命名旧文件,以便以后可以恢复它。)。
接下来,打开系统设置中的“区域语言”并尝试更改语言。对话框会冻结,因此只需忽略它并返回终端即可。此时,一个名为Account-daemon的程序占用了100%的CPU内核,因此您的计算机可能会变得很慢,并开始变得很热。
在终端中,删除符号链接。否则你可能会把自己锁在自己的账户之外!
下一步是向帐户守护进程发送SIGSTOP信号,以阻止它冲击CPU核心。但要做到这一点,您首先需要知道帐户守护进程的进程标识符(PID)。在视频中,我通过运行top来实现这一点,top是一个用于监视正在运行的进程的实用程序。因为帐户守护程序陷入无限循环,所以它很快就会出现在列表的顶部。另一种查找PID的方法是使用pidof实用程序:
有了帐户守护程序的PID,您可以使用KILL发送SIGSTOP信号:
这是关键的一步。您将注销您的帐户,但首先您需要设置一个计时器,以便在您注销后重置帐户守护进程。否则,你只会被锁在门外,攻击就会失败。(如果发生这种情况,不用担心:重启后一切都会恢复正常。)。以下是如何设置计时器:
Nohup实用程序是在您注销后让脚本保持运行的一种简单方式。此命令告诉它运行一个bash脚本,该脚本执行三项操作:
睡30秒。(您只需要给自己足够的时间注销。我将视频设置为10秒。)。
向帐户守护程序发送SIGCONT信号以停用您之前发送的SIGSTOP。在收到SIGCONT之前,SIGSEGV不会生效。
完成后,注销并等待几秒钟,等待SIGSEGV引爆。如果攻击成功,您将看到一系列用于创建新用户帐户的对话框。新用户帐户是管理员帐户。(在视频中,我运行id以显示新用户是sudo组的成员,这意味着它拥有根权限。)。
跟着我!即使你事先不知道Ubuntu(或者更具体地说,GNOME)是如何工作的,我想我可以向你解释这个漏洞。实际上涉及到两个错误。第一个是帐户服务,这是一项管理计算机上用户帐户的服务。第二个是在GNOME显示管理器(Gdm3)中,除其他功能外,它还处理登录屏幕。下面我将分别解释这些错误。
ACCOUNTS服务守护程序(ACCOUNTS-DAEMON)是管理计算机上的用户帐户的系统服务。它可以做一些事情,比如创建一个新的用户账户或更改用户的密码,但它也可以做一些不太安全敏感的事情,比如更改用户的图标或他们的首选语言。守护程序是在后台运行的程序,没有自己的用户界面。但是,系统设置对话框可以通过称为D-BUS的消息系统与帐户守护程序通信。
在利用漏洞攻击中,我使用系统设置对话框更改语言。标准用户可以自行更改该设置-不需要管理员权限。在幕后,系统服务对话框会通过D-BUS将org.freedesktop.Accounts.User.SetLanguage命令发送到Account-daemon。
事实证明,Ubuntu使用了修改后的Account服务版本,其中包含一些额外的代码,这些代码在freedesktop维护的上游版本中是不存在的。Ubuntu的补丁添加了一个名为is_in_pam_Environment的函数,该函数在用户的主目录中查找名为.pam_Environment的文件并读取该文件。该拒绝服务漏洞是通过使.pam_Environment成为指向/dev/零的符号链接来实现的。/dev/zero是一个特殊文件,实际上并不存在于磁盘上。它由操作系统提供,其行为就像一个无限长的文件,其中每个字节都是零。当is_in_pam_Environment尝试读取.pam_Environment时,它会被符号链接重定向到/dev/ero,然后陷入无限循环,因为/dev/零是无限长的。
这个错误还有第二个部分。该攻击包括通过向帐户守护程序发送SIGSEGV使其崩溃。一个标准用户当然不应该被允许那样使系统服务崩溃?它们不应该这样做,但是帐户守护程序在开始读取用户的.pam_Environment之前无意中删除了权限,从而允许了这种情况。删除权限意味着守护进程暂时丧失其超级用户权限,取而代之的是用户的较低权限。具有讽刺意味的是,这是一种安全预防措施,其目标是保护守护程序不受恶意用户的攻击,恶意用户会将他们的.pam_Environment符号链接到/etc/dow,这是标准用户不允许读取的高度敏感的文件。不幸的是,如果操作不正确,它还会授予用户发送守护程序信号的权限,这就是我们能够向帐户守护程序发送SIGSEGV的原因。
GNOME显示管理器(Gdm3)是Ubuntu用户界面的基本组件。它可以处理在用户登录和注销时启动和停止用户会话等操作。它还管理登录屏幕。
GDM3处理的另一件事是新计算机的初始设置。当你在一台新电脑上安装Ubuntu时,你需要做的第一件事就是创建一个用户账户。初始用户帐户需要是管理员,这样您才能继续设置机器、执行配置wifi和安装应用程序等操作。以下是初始设置屏幕的屏幕截图(取自利用漏洞视频):
您在屏幕截图中看到的对话框是一个单独的应用程序,名为gnome-Initial-Setup。当系统上没有用户帐户时,它由gdm3触发,这是新计算机初始设置期间的预期场景。Gdm3如何检查系统上有多少用户?您可能已经猜到了:通过询问帐户-守护程序!那么,如果帐户守护程序没有响应,会发生什么呢?相关代码在这里。
它使用D-BUS询问帐户守护程序有多少用户,但由于帐户守护程序没有响应,D-BUS方法调用因超时而失败。(在我的测试中,超时花了大约20秒。)。由于超时错误,代码未设置Priv->;Have_Existing_User_Account的值。不幸的是,priv->;Have_Existing_USER_ACCOUNTS的缺省值是FALSE,而不是TRUE,所以现在gdm3认为没有用户帐户,并启动gnome-Initial-Setup。
我必须承认:我发现这个漏洞完全是偶然的。这是我在英国夏令时10月14日晚上10点左右发给同事的信息:
我只是偶然得到了LPE,但我不太确定如何复制它。🤦。
事情是这样的:我在账户服务中发现了几个拒绝服务漏洞。我认为它们的严重性不高,但正在写一份漏洞报告,准备发送给Ubuntu。下午6点左右,我停下了工作,关上了笔记本电脑的盖子。晚上晚些时候,我打开笔记本电脑的盖子,发现我的账户被锁在了门外。我一直在尝试使用.pam_Environment符号链接,但忘记在盖上盖子之前将其删除。没什么大不了的:我使用Ctrl-Alt-F4打开控制台,登录(控制台登录不受帐户服务DOS的影响),并用SIGSEGV杀死了帐户守护进程。由于特权删除漏洞,我不需要使用sudo。接下来,我查看了GNOME-Initial-Setup对话框,惊讶地发现我能够创建一个具有管理员权限的新用户。
不幸的是,当我试图重现相同的步骤序列时,我无法让它再次工作。我检查了系统日志以寻找线索,但没有太多信息,因为我没有启用GDM的调试消息。我后来开发的漏洞需要用户注销他们的账户,但我绝对没有在10月14日晚上这样做。所以那天晚上我是如何意外触发漏洞的仍然是个谜。
那天晚上晚些时候,我给我(在美国)的同事发了进一步的信息,描述了发生的事情。谈论这些对话框有助于唤起我对最近注意到的一些事情的记忆。我一直关注的许多系统服务都使用策略包来检查客户端是否有权请求操作。我注意到一个名为gnome-Initial-setup.pkla的文件,它是一个策略工具包配置文件,允许名为gnome-Initial-Setup的用户执行许多安全敏感的操作,例如挂载文件系统和创建新的用户帐户。于是我对同事们说:“我想知道这是否与GNOME初始设置有关。”巴斯·艾伯茨(Bas Alberts)几乎立刻提出了一个假设,事实证明这个假设是正确的:“我认为,你欺骗GDM启动了GNOME初始设置,如果GDM会话无法验证账户是否已经存在,这种情况可能会发生。”
在那之后,只需在gdm3中找到触发gnome初始设置的代码,并弄清楚如何在帐户守护程序没有响应的情况下触发它。我发现当用户注销时会触发相关代码。
这就是我一天的工作结束是零日的开始的故事!