← Back to team overview

dx-packages team mailing list archive

[Bug 1900255] Re: accountsservice drop privileges denial of service (GHSL-2020-187, GHSL-2020-188)

 

** Information type changed from Private Security to Public Security

-- 
You received this bug notification because you are a member of DX
Packages, which is subscribed to accountsservice in Ubuntu.
Matching subscriptions: dx-packages
https://bugs.launchpad.net/bugs/1900255

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

Status in accountsservice package in Ubuntu:
  Fix Released

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 `securitylab@xxxxxxxxxx`
  (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 `securitylab@xxxxxxxxxx`, 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).

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/accountsservice/+bug/1900255/+subscriptions