accountsservice drop privileges denial of service (GHSL-2020-187, GHSL-2020-188)

Bug #1900255 reported by kev
256
This bug affects 1 person
Affects Status Importance Assigned to Milestone
accountsservice (Ubuntu)
Fix Released
Undecided
Unassigned

Bug Description

# GitHub Security Lab (GHSL) Vulnerability Report: `GHSL-2020-187`, `GHSL-2020-188`

The [GitHub Security Lab](https://securitylab.github.com) team has identified potential security vulnerabilities in [accountsservice](https://git.launchpad.net/ubuntu/+source/accountsservice/).

We are committed to working with you to help resolve these issues. In this report you will find everything you need to effectively coordinate a resolution of these issues with the GHSL team.

If at any point you have concerns or questions about this process, please do not hesitate to reach out to us at `<email address hidden>` (please include `GHSL-2020-187` or `GHSL-2020-188` as a reference).

If you are _NOT_ the correct point of contact for this report, please let us know!

## Summary

The accountsservice daemon drops privileges to perform certain operations. For example while performing the `org.freedesktop.Accounts.User.SetLanguage` D-Bus method, which can be triggered by an unprivileged user, accounts-daemon temporarily drops privileges to the same UID as the user, to avoid being tricked into opening a file which the unprivileged user should not be able to access. Unfortunately, by changing its [RUID](https://en.wikipedia.org/wiki/User_identifier#Real_user_ID) it has given the user permission to send it signals. This means that the unprivileged user can send accounts-daemon a `SIGSTOP` signal, which stops the process and causes a denial of service.

## Product

[accountsservice](https://git.launchpad.net/ubuntu/+source/accountsservice/)

## Tested Version

* accountsservice, version 0.6.55-0ubuntu12~20.04.1
* Tested on Ubuntu 20.04.1 LTS

Note: I believe these issues only exist in Ubuntu's version of accountsservice. I couldn't find the vulnerable functions in the git repos maintained by [freedesktop](https://gitlab.freedesktop.org/accountsservice/accountsservice) or [debian](https://salsa.debian.org/freedesktop-team/accountsservice). I originally discovered the vulnerable code in the version of the code that I had obtained by running `apt-get source accountsservice`, but I struggled to figure out where it came from when I started searching the official repositories. I eventually tracked it down to this patch file: [0010-set-language.patch](https://git.launchpad.net/ubuntu/+source/accountsservice/tree/debian/patches/0010-set-language.patch?h=ubuntu/focal-updates&id=e0347185d4c5554b026c13ccca691577c239afd5).

## Details

### Issue 1: accountsservice drop privileges `SIGSTOP` denial of service (`GHSL-2020-187`)

A [source code patch](https://git.launchpad.net/ubuntu/+source/accountsservice/tree/debian/patches/0010-set-language.patch?h=ubuntu/focal-updates&id=e0347185d4c5554b026c13ccca691577c239afd5) that (as far as I know) only exists in Ubuntu's version of accountsservice, adds a function named [`user_drop_privileges_to_user`](https://git.launchpad.net/ubuntu/+source/accountsservice/tree/debian/patches/0010-set-language.patch?h=ubuntu/focal-updates&id=e0347185d4c5554b026c13ccca691577c239afd5#n66):

```c
static gboolean
user_drop_privileges_to_user (User *user)
{
        if (setresgid (user->gid, user->gid, -1) != 0) {
                g_warning ("setresgid() failed");
                return FALSE;
        }
        if (setresuid (accounts_user_get_uid (ACCOUNTS_USER (user)), accounts_user_get_uid (ACCOUNTS_USER (user)), -1) != 0) {
                g_warning ("setresuid() failed");
                return FALSE;
        }
        return TRUE;
}
```

This function is used to drop privileges while doing operations on behalf of an unprivileged user. Dropping the [EUID](https://en.wikipedia.org/wiki/User_identifier#Effective_user_ID) is a sensible precaution, which prevents accountsservice from accessing a file which the unprivileged user cannot access themselves. Unfortunately, dropping the [RUID](https://en.wikipedia.org/wiki/User_identifier#Real_user_ID) has the opposite effect of making security worse, because it enables the unprivileged user to send signals to accountsservice. For example, they can send a `SIGSTOP` which stops the accountsservice daemon and causes a denial of service.

The vulnerability can be triggered via multiple different D-Bus methods. Many of them involve precise timing to send the `SIGSTOP` signal at just the right moment. But there is a much simpler and more reliable way to reproduce the vulnerability by combining the privilege dropping vulnerability (`GHSL-2020-187`) with the infinite loop vulnerability (`GHSL-2020-188`), which is described next. So please see the description of `GHSL-2020-188`, below, for a proof-of-concept that triggers both vulnerabilities.

#### Impact

An unprivileged local user can cause a local denial of service that affects all users of the system. Stopping the accountsservice daemon prevents the login screen from working, because `gdm3` needs to talk to accounts-daemon (via D-Bus).

Unfortunately, the impact is worse than just local denial of service, because this vulnerability can be chained with a separate vulnerability in gdm3 to achieve privilege escalation. Please see the description of `GHSL-2020-188`, below, for a proof-of-concept of the privilege escalation vulnerability.

#### Remediation

When dropping privileges, only drop the EUID and not the RUID.

### Issue 2: accountsservice `.pam_environment` infinite loop (GHSL-2020-188)

A [source code patch](https://git.launchpad.net/ubuntu/+source/accountsservice/tree/debian/patches/0010-set-language.patch?h=ubuntu/focal-updates&id=e0347185d4c5554b026c13ccca691577c239afd5) that (as far as I know) only exists in Ubuntu's version of accountsservice, adds a function named [`is_in_pam_environment`](https://git.launchpad.net/ubuntu/+source/accountsservice/tree/debian/patches/0010-set-language.patch?h=ubuntu/focal-updates&id=e0347185d4c5554b026c13ccca691577c239afd5#n366):

```c
static gboolean
is_in_pam_environment (User *user,
                       const gchar *property)
{
        gboolean ret = FALSE;
        const gchar *prefix;
        FILE *fp;
        g_autofree gchar *pam_env = NULL;

        if (g_strcmp0 (property, "Language") == 0)
                prefix = "LANG";
        else if (g_strcmp0 (property, "FormatsLocale") == 0)
                prefix = "LC_TIME";
        else
                return FALSE;

        pam_env = g_build_path ("/", accounts_user_get_home_directory (ACCOUNTS_USER (user)), ".pam_environment", NULL);

        if (!user_drop_privileges_to_user (user))
                return FALSE;
        if ((fp = fopen (pam_env, "r"))) {
                gchar line[50];
                while ((fgets (line, 50, fp)) != NULL) {
                        if (g_str_has_prefix (line, prefix)) {
                                ret = TRUE;
                                break;
                        }
                }
                fclose (fp);
        }
        user_regain_privileges ();

        return ret;
}
```

This function parses the contents of a file named `.pam_environment` in the (unprivileged) user's home directory. The user can trigger an infinite loop by creating a symlink to `/dev/zero`:

```bash
ln -s /dev/zero ~/.pam_environment
```

The infinite loop can then be triggered by sending a D-Bus message to the accountsservice daemon:

```bash
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 &
```

Because the accountsservice daemon is now stuck in an infinite loop, and has dropped privileges, it is now easy to also demonstrate `GHSL-2020-187`:

```bash
kill -SIGSTOP `pidof accounts-daemon`
```

The accountsservice daemon is now unresponsive, which means that the GNOME login screen no longer works. Unfortunately, this vulnerability can be chained with a separate vulnerability in gdm3 (which I have reported simultaneously to GNOME) to get privilege escalation. The steps (tested on Ubuntu Desktop 20.04.1 LTS) are as follows:

* An unprivileged user logs into their account.
* They send a `SIGSTOP` to accountsservice, using the instructions above.
* They log out of their account.
* They open a text console (usually with a key combination such as Ctrl-Alt-F3).
* They login to the text console.
* They send accounts-daemon a `SIGSEGV`, followed by a `SIGCONT`, which causes accounts-daemon to crash.
* Because the accountsservice daemon was unresponsive, gdm3 is under the mistaken impression that there are zero user accounts on the system. So it triggers `gnome-initial-setup`, because it thinks this is a first-time installation.
* The user clicks through the `gnome-initial-setup` dialog boxes and creates a new account for themselves. The new account is a member of the `sudo` group.

I have made a video of this proof-of-concept, which you can see [here](https://drive.google.com/file/d/1rpRbXW1PRKMKRcxft3x-9tprjEbHLgOQ/view?usp=sharing). The video is only visible to people who have the link, so it as safe as long as you are careful who you share the link with.

#### Impact

An unprivileged local user can cause a local denial of service that affects all users of the system. Making the accountsservice daemon unresponsive prevents the login screen from working, because `gdm3` needs to talk to accounts-daemon (via D-Bus).

Unfortunately, the impact is worse than just local denial of service, because this vulnerability can be chained with a separate vulnerability in gdm3 to achieve privilege escalation, as explained in the proof-of-concept above.

#### Remediation

I would recommend using `fstat` to check that the file is a regular file before reading it.

## Credit

These issues were discovered and reported by GHSL team member [@kevinbackhouse (Kevin Backhouse)](https://github.com/kevinbackhouse).

## Contact

You can contact the GHSL team at `<email address hidden>`, please include a reference to `GHSL-2020-187` or `GHSL-2020-188`in any communication regarding this issue.

## Disclosure Policy

This report is subject to our [coordinated disclosure policy](https://securitylab.github.com/disclosures#policy).

CVE References

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

Thanks for reporting these issues, we are currently investigating them and will be assigning CVEs shortly.

Revision history for this message
Seth Arnold (seth-arnold) wrote :

Hello Kevin, nice discovery.

Please use:

CVE-2020-16126 for GHSL-2020-187
CVE-2020-16127 for GHSL-2020-188

Thanks

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

Would 2020-11-03 be ok as a CRD for this issue? Thanks!

Revision history for this message
kev (kbackhouse2000) wrote :

Thanks! 2020-11-03 is great.

Revision history for this message
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package accountsservice - 0.6.55-0ubuntu13.2

---------------
accountsservice (0.6.55-0ubuntu13.2) groovy-security; urgency=medium

  * SECURITY UPDATE: accountsservice drop privileges SIGSTOP DoS
    (LP: #1900255)
    - debian/patches/0010-set-language.patch: updated to not drop real uid
      and real gid in user_drop_privileges_to_user.
    - debian/patches/0009-language-tools.patch: updated to not reset
      effective uid.
    - CVE-2020-16126
  * SECURITY UPDATE: accountsservice .pam_environment infinite loop
    (LP: #1900255)
    - debian/patches/0010-set-language.patch: updated to use O_NOFOLLOW
      and limit the number of lines read from file.
    - CVE-2020-16127

 -- Marc Deslauriers <email address hidden> Mon, 02 Nov 2020 12:02:46 -0500

Changed in accountsservice (Ubuntu):
status: New → Fix Released
Revision history for this message
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package accountsservice - 0.6.55-0ubuntu12~20.04.4

---------------
accountsservice (0.6.55-0ubuntu12~20.04.4) focal-security; urgency=medium

  * SECURITY UPDATE: accountsservice drop privileges SIGSTOP DoS
    (LP: #1900255)
    - debian/patches/0010-set-language.patch: updated to not drop real uid
      and real gid in user_drop_privileges_to_user.
    - debian/patches/0009-language-tools.patch: updated to not reset
      effective uid.
    - CVE-2020-16126
  * SECURITY UPDATE: accountsservice .pam_environment infinite loop
    (LP: #1900255)
    - debian/patches/0010-set-language.patch: updated to use O_NOFOLLOW
      and limit the number of lines read from file.
    - CVE-2020-16127

 -- Marc Deslauriers <email address hidden> Mon, 02 Nov 2020 12:03:54 -0500

Changed in accountsservice (Ubuntu):
status: New → Fix Released
Revision history for this message
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package accountsservice - 0.6.45-1ubuntu1.3

---------------
accountsservice (0.6.45-1ubuntu1.3) bionic-security; urgency=medium

  * SECURITY UPDATE: accountsservice drop privileges SIGSTOP DoS
    (LP: #1900255)
    - debian/patches/0010-set-language.patch: updated to not drop real uid
      and real gid in user_drop_privileges_to_user.
    - debian/patches/0009-language-tools.patch: updated to not reset
      effective uid.
    - CVE-2020-16126
  * SECURITY UPDATE: directory traversal issue
    - debian/patches/CVE-2018-14036.patch: fix insufficient path prefix
      check in src/user.c.
    - CVE-2018-14036

 -- Marc Deslauriers <email address hidden> Mon, 02 Nov 2020 12:05:51 -0500

Changed in accountsservice (Ubuntu):
status: New → Fix Released
information type: Private Security → Public Security
To post a comment you must log in.
This report contains Public Security information  
Everyone can see this security related information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.