Warning
The author does not maintain this report actively. Please see their reports on newer versions of Fedora Linux for up-to-date information and corrections of errors.
by OK Ryoko, revision 2024-11-28.1
Assumed audience: Linux system administrators, Linux utility authors and Fedora Linux package maintainers. Familiarity with credentials, capabilities, syscalls, strace, Linux PAM and SELinux is assumed.
In this report, I build on the work described in SUID-root Binaries in Fedora Workstation 38. I focus on identifying the file capabilities needed to limit the level of privilege attainable by the new SUID-root binaries in Fedora Workstation 39.
I provide a high-level summary of outcomes in the “The findings at a glance” section.
Appendix A expands the abbreviations that appear in this report.
Appendix B presents metadata for the SUID-root binaries not covered in the main text.
- Characterization of environments
- Conventions for presenting results
- The SUID-root binaries in detail
- The findings at a glance
- Supporting information
- Feedback and support
- Funding sources
- Licensing
- Revision history
My findings are for an installation of Fedora Workstation 39 1.5, virtualized using the libvirt 9.0.0 API with the QEMU/KVM driver from the image obtained via the following torrent:
https://torrent.fedoraproject.org/torrents/Fedora-Workstation-Live-x86_64-39.torrent
This image had SHA256 checksum af52046e43c6f06afd3456d2a9a36dd9782fcb204f05a21b1c31f593db36a8e8.
When creating the virtual machine, I added a virtiofs shared file system so that I could extract traces and logs easily.
Here’s some basic system information:
user@fedora:~$ uname -a # a: all
Linux fedora 6.5.6-300.fc39.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Oct 6 19:57:21 UTC 2023 x86_64 GNU/Linux
SELinux is enabled and enforcing:
user@fedora:~$ sestatus
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Memory protection checking: actual (secure)
Max kernel policy version: 33
The selinux-policy-targeted-38.28-1.fc39.noarch package provides the loaded policy.
On the first boot of the new system, I created a single unprivileged and unconfined user who was configured automatically:
user@fedora:~$ id
uid=1000(user) gid=1000(user) groups=1000(user),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
By default, the Anaconda installer locks the root account:
user@fedora:~$ sudo passwd -S root # S: status
root LK 1969-12-30 0 99999 7 -1 (Password locked.)
In a virtual console, my login shell had all capabilities in the bounding set and one new capability in the inheritable set:
[user@fedora ~]$ cat /proc/$$/status | grep '^Cap'
CapInh: 0000000800000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
[user@fedora ~]$ capsh --decode=0000000800000000
0x0000000800000000=cap_wake_alarm
The presence of CAP_WAKE_ALARM
in the inheritable set is due to systemd pull request #26548, the purpose of which is to allow user-space applications to wake the system up from suspension.
login(1) runs the PAM stack at /etc/pam.d/login, which includes pam_systemd
. pam_systemd
adds CAP_WAKE_ALARM
to both the inheritable and ambient sets. login(1) then fork(2)s and calls setuid(2) to change to nonzero UIDs, clearing the permitted, effective and ambient sets, and therefore leaving only CAP_WAKE_ALARM
in the inheritable set of the child process.
The inheritable set can be cleared without privileges using capsh(1):
[user@fedora ~]$ capsh --inh= --
[user@fedora ~]$ getpcaps $$
1799: =
On the other hand, nonlogin shells under GNOME Terminal had a full bounding capability set and no other capabilities:
user@fedora:~$ cat /proc/$$/status | grep '^Cap'
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
Why are the inheritable and ambient sets empty in this case? Processes started in GNOME are children of a per-user systemd(1) instance, and systemd(1) clears the inheritable and ambient sets prior to performing an execve(2). (The permitted and effective sets are cleared automatically when a process with nonzero UIDs performs an execve(2).)
In either case, no securebits base or locked flags had been set. For example, in a login shell:
[user@fedora ~]$ capsh --print | grep '^Securebits'
Securebits: 00/0x0/1'b0 (no-new-privs=0)
I didn’t upgrade any packages on the system explicitly after the installation procedure.
I installed the following packages from the fedora repository to gain access to utilities for my analyses:
- bcc-0.27.0-4.fc39.x86_64 for the BPF Compiler Collection
- bcc-tools-0.27.0-4.fc39.x86_64 for the capable utility
- fuse-devel-2.9.9-17.fc39.x86_64 and fuse3-devel-3.16.1-1.fc39.x86_64 for FUSE development headers
- gcc-13.2.1-6.fc39.x86_64 for the GNU Compiler Collection
- pam-devel-1.5.3-2.fc39.x86_64 for PAM development headers
- setools-console-4.4.3-1.fc39.x86_64 for sesearch(1) and seinfo(1)
- strace-6.6-1.fc39.x86_64 for strace(1)
My first time using dnf(8) to install packages, I was asked whether to import the following GPG key:
Importing GPG key 0x18B8E74C:
Userid : "Fedora (39) <[email protected]>"
Fingerprint: E8F2 3996 F232 1864 0CB4 4CBE 75CF 5AC4 18B8 E74C
From : /etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-39-x86_64
Is this ok [y/N]:
I gave my assent after comparing the fingerprint to the value listed on https://fedoraproject.org/security/ for equality.
Finally, I disabled automatic software updates in GNOME Software to preserve the environment.
I examined the userhelper(8) program in the live environment, which differs from the system installed to a disk as follows:
- the host name is
localhost-live
; - the user name is
liveuser
, and - the root account is unlocked and has an empty password.
I obfuscated timestamps because they were irrelevant to the analyses.
I omitted all sudo(8) password prompts for brevity.
I normalized all hard tabs to spaces in command output.
I always had strace(1) write output to a file using the --output
option, which I omitted from reported commands because it doesn’t change the content of the trace.
Likewise, I always redirected the output of tail(1)–grep(1) pipelines to a file.
Prompts for commands come in two flavors: [user@fedora ~]$
means I’m in a login shell and user@fedora:~$
means I’m in a nonlogin shell.
In a virtual console (tty2), I searched the root file system for SUID-root binaries using find(1):
[user@fedora ~]$ sudo find / -ignore_readdir_race -perm /u=s -user root
/usr/bin/chage
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/fusermount
/usr/bin/fusermount-glusterfs
/usr/bin/fusermount3
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/pkexec
/usr/bin/su
/usr/bin/sudo
/usr/bin/umount
/usr/bin/vmware-user-suid-wrapper
/usr/lib/polkit-1/polkit-agent-helper-1
/usr/libexec/dbus-1/dbus-daemon-launch-helper
/usr/libexec/openssh/ssh-keysign
/usr/libexec/spice-gtk-x86_64/spice-client-glib-usb-acl-helper
/usr/libexec/Xorg.wrap
/usr/libexec/libgtop_server2
/usr/libexec/qemu-bridge-helper
/usr/sbin/grub2-set-bootflag
/usr/sbin/mount.nfs
/usr/sbin/pam_timestamp_check
/usr/sbin/unix_chkpwd
/usr/sbin/userhelper
Of these 27 results, 2 binaries had not been present on an installation of Fedora Workstation 38 1.6:
/usr/bin/chfn
/usr/bin/chsh
Unless stated otherwise, all these binaries have owner root
, group root
and mode -rwsr-xr-x
with no extended attributes other than security.selinux
.
I first discuss how changes to the binaries I had already investigated impact the reference use cases in SUID-root Binaries in Fedora Custom OS 38 and SUID-root Binaries in Fedora Workstation 38. I then examine the two new SUID-root binaries, /usr/bin/chfn and /usr/bin/chsh, in detail. These binaries had previously been provided by the util-linux-user package and are now provided by the util-linux package.
I defer mention of binaries that haven’t changed significantly since Fedora Workstation 38 with respect to my reference use cases to Appendix B.
- Program to change a user’s finger information, i.e., the GECOS field in /etc/passwd
- Provided by util-linux-2.39.2-1.fc39.x86_64
- Installed with mode
-rws--x--x
- Has SHA256 checksum 79f263d701c75adb7a707353b0f236fd67a5bee62150f8fe74337ff9e90767ae
- Has security context
system_u:object_r:chfn_exec_t:s0
set by the usermanage 1.19.0 module
chfn(1) must be able to update /etc/passwd. This involves creating a temporary file at /etc/.chfn.XXXXXX (CAP_DAC_OVERRIDE
), changing the owner and group of that file (CAP_CHOWN
) and changing the mode of that file (CAP_FOWNER
).1 chfn(1) must also be able to run the following PAM stack:
#%PAM-1.0
auth sufficient pam_rootok.so
auth include system-auth
account include system-auth
password include system-auth
session include system-auth
Source:
cat /etc/pam.d/chfn
Therefore, chfn(1) should also be able to write to the audit log (CAP_AUDIT_WRITE
).
By default, chfn(1) is restricted from changing the finger information by the configuration in /etc/login.defs. I therefore updated /etc/login.defs like so:
[user@fedora ~]$ sudo sed -i -e 's/^#CHFN_RESTRICT.*/CHFN_RESTRICT fr/' /etc/login.defs # i: in-place, e: expression
This change would allow me to set the full name and office room number.
Next, I retrieved the current GECOS field:
[user@fedora ~]$ cat /etc/passwd | grep '^user' | cut -d ':' -f 5 # d: delimiter, f: fields
Fedora User
I then tried setting the office room number to the current Fedora Linux release number:
[user@fedora ~]$ chfn -o 39 user # o: office
Changing finger information for user.
Password:
Finger information changed.
[user@fedora ~]$ cat /etc/passwd | grep '^user' | cut -d ':' -f 5
Fedora User,39
I successfully reproduced this step after making the following modifications:
[user@fedora ~]$ sudo chmod u-s /usr/bin/chfn
[user@fedora ~]$ sudo setcap cap_chown,cap_dac_override,cap_fowner,cap_audit_write=ep /usr/bin/chfn
The audit log showed three successes and zero failures:
type=USER_AUTH msg=audit(0.0:228): pid=2000 uid=1000 auid=1000 ses=2 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='op=PAM:authentication grantors=pam_usertype,pam_localuser,pam_unix acct="user" exe="/usr/bin/chfn" hostname=fedora addr=? terminal=tty2 res=success'UID="user" AUID="user"
type=USER_ACCT msg=audit(0.0:229): pid=2000 uid=1000 auid=1000 ses=2 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='op=PAM:accounting grantors=pam_unix,pam_localuser acct="user" exe="/usr/bin/chfn" hostname=fedora addr=? terminal=tty2 res=success'UID="user" AUID="user"
type=CRED_ACQ msg=audit(0.0:230): pid=2000 uid=1000 auid=1000 ses=2 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='op=PAM:setcred grantors=pam_localuser,pam_unix acct="user" exe="/usr/bin/chfn" hostname=fedora addr=? terminal=tty2 res=success'UID="user" AUID="user"
Source:
sudo tail -n 64 /var/log/audit/audit.log | grep 'chfn'
, where-n
is short for--lines
, after omitting messages for the SUID-root run of chfn(1)
As a confidence check, I tried (unsuccessfully) to set a field I shouldn’t be able to set…
[user@fedora ~]$ chfn -p '(888) 555-0181' user # p: office-phone
chfn: login.defs forbids setting Office Phone
… as well as change the finger information of another user:
[user@fedora ~]$ sudo useradd noriko
[user@fedora ~]$ chfn -f 'Noriko Takaya' noriko # f: full-name
chfn: running UID doesn't match UID of user we're altering, change denied: Permission denied
As far as I can tell from the source code and strace(1) output, chfn(1) doesn’t drop privileges explicitly by calling any of setuid(2), seteuid(2), setreuid(2) or setresuid(2).
There are no transitions from the unconfined_t
domain through the chfn_exec_t
file type:
[user@fedora ~]$ sudo sesearch -T -s unconfined_t -t chfn_exec_t # T: type_trans, s: source, t: target
[user@fedora ~]$
Thus, chfn(1) runs unconfined.
If unprivileged users should not be able to change their finger information, then consider unsetting the SUID bit on /usr/bin/chfn.
All line numbers refer to points in source code after applying the patches in src.fedoraproject.org/rpms/util-linux@59309f2.
The HAVE_MKOSTEMP
and HAVE_LIBUSER
macros are assumed to be defined and undefined, respectively.
1 In util-linux/login-utils/[email protected], main
calls setpwnam
, passing ".chfn"
for the prefix
argument (line 384). In util-linux/login-utils/[email protected], setpwnam
calls xfmkstemp
to create the temporary file at /etc/.chfn.XXXXXX (line 87), then fchown(2) on that file (line 91) and then fchmod(2) on that file (line 143). Finally, setpwnam
renames /etc/.chfn.XXXXXX via rename(2), passing PASSWD_FILE
as the new path (line 163). In util-linux/login-utils/[email protected], PASSWD_FILE
is a macro defined as the _PATH_PASSWD
macro (line 20) that, according to line 60 of util-linux/include/[email protected], expands to "/etc/passwd"
. In util-linux/include/[email protected], xfmkstemp
wraps xmkstemp
(line 25). In util-linux/lib/[email protected], xmkstemp
wraps mkstemp_cloexec
(line 70), which wraps mkostemp(3) (line 24).
- Program to change a user’s login shell
- Provided by util-linux-2.39.2-1.fc39.x86_64
- Installed with mode
-rws--x--x
- Has SHA256 checksum 17a452909206b56469c58242b98fe6d49e9f41752ece0d63ead3bcc64882e5c6
- Has security context
system_u:object_r:chfn_exec_t:s0
set by the usermanage 1.19.0 module
Yes. chfn(1) and chsh(1) both create a temporary file with which to update /etc/passwd using the same mechanism,1 and both run an identical PAM stack. I was able to change my login shell after making the same modifications as I did to /usr/bin/chfn:
[user@fedora ~]$ cat /etc/passwd | grep '^user' | cut -d ':' -f 7
/bin/bash
[user@fedora ~]$ sudo chmod u-s /usr/bin/chsh
[user@fedora ~]$ sudo setcap cap_chown,cap_dac_override,cap_fowner,cap_audit_write=ep /usr/bin/chsh
[user@fedora ~]$ chsh -s /bin/sh user
Changing shell for user.
Password:
Shell changed.
[user@fedora ~]$ cat /etc/passwd | grep '^user' | cut -d ':' -f 7
/bin/sh
As a confidence check, I tried (unsuccessfully) to change the login shell of the user I created while investigating /usr/bin/chfn:
[user@fedora ~]$ chsh -s /bin/sh noriko
chsh: running UID doesn't match UID of user we're altering, shell change denied: Permission denied
If unprivileged users should not be able to change their login shell, then consider unsetting the SUID bit on /usr/bin/chsh.
The HAVE_LIBUSER
macro is assumed to be undefined.
1 In util-linux/login-utils/[email protected], main
calls setpwnam
, passing ".chsh"
for the prefix
argument (line 297). setpwnam
is described in more detail in the corresponding citation for /usr/bin/chfn.
- Program to administer user groups
- Provided by shadow-utils-2:4.14.0-1.fc39.x86_64
- Has SHA256 checksum fa320ced9d199d45bb6f88c15a5c80d38de531c208db3ddf2730fd33686c5735
- Has security context
system_u:object_r:groupadd_exec_t:s0
set by the usermanage 1.19.0 module
The corresponding procedure outlined in SUID-root Binaries in Fedora Custom OS 38 is reproducible in Fedora Workstation 39.
strace(1) output reveals the following failing syscall:
1863<gpasswd> prlimit64(0, RLIMIT_NOFILE, {rlim_cur=RLIM64_INFINITY, rlim_max=RLIM64_INFINITY}, NULL) = -1 EPERM (Operation not permitted)
Source:
sudo strace -u user -fyY gpasswd -a noriko gunbuster
, where-u
is short for--user
,-fyY
for--follow-forks --decode-fds=path --decode-pids=comm
and-a
for--add
This error occurs because gpasswd(1) tries to set the hard limit on the maximum file descriptor that gpasswd(1) can open above the value in /proc/sys/fs/nr_open. Neither setting CAP_SYS_RESOURCE
on /usr/bin/gpasswd nor running gpasswd(1) SUID-root ensures the success of this operation.
- Program to mount file systems
- Provided by util-linux-core-2.39.2-1.fc39.x86_64
- Has SHA256 checksum a709f0807a33e526058f52f914793aa1bb4cff472592bd2fb9cba55248980534
- Has security context
system_u:object_r:mount_exec_t:s0
set by the mount 1.16.1 module
The corresponding procedures outlined in SUID-root Binaries in Fedora Custom OS 38 and SUID-root Binaries in Fedora Workstation 38 are reproducible in Fedora Workstation 39.
libmount(3) now uses the new mount API introduced in Linux 5.2. On Fedora Workstation 39, mount(8) traces will therefore contain calls to, e.g., fsconfig(2), fsmount(2) and move_mount(2), rather than mount(2):
1984<mount> fsconfig(3<anon_inode:[fscontext]>, FSCONFIG_SET_STRING, "source", "/dev/vda1", 0) = 0
1984<mount> fsconfig(3<anon_inode:[fscontext]>, FSCONFIG_CMD_CREATE, NULL, NULL, 0) = 0
1984<mount> fsmount(3<anon_inode:[fscontext]>, FSMOUNT_CLOEXEC, 0) = 4</>
1984<mount> statx(4</>, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_MNT_ID, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=STATX_ATTR_MOUNT_ROOT, stx_mode=S_IFDIR|0755, stx_size=6, ...}) = 0
1984<mount> mount_setattr(4</>, "", AT_EMPTY_PATH, {attr_set=MOUNT_ATTR_NOSUID|MOUNT_ATTR_NODEV, attr_clr=0, propagation=0 /* MS_??? */, userns_fd=0}, 32) = 0</dev/pts/0>
1984<mount> move_mount(4</>, "", AT_FDCWD</home/user>, "/mnt/upart", MOVE_MOUNT_F_EMPTY_PATH) = 0
Source:
sudo strace -u user -fyY mount /mnt/upart
mount(8) tries to drop privileges by calling setuid(2) to change the UIDs to the RUID. This call will set the EUID to the RUID and clear the effective set, but will not affect the saved SUID unless mount(8) has CAP_SETUID
in its effective set. In principle, mount(8) could later call setuid(2) to restore the EUID to 0. Accordingly, CAP_SETUID
should be set on /usr/bin/mount so that mount(8) can drop privileges properly.
- Program to change the GIDs during a login session
- Provided by shadow-utils-2:4.14.0-1.fc39.x86_64
- Has SHA256 checksum 0758c5d07299fcf52d7d907350b91c02fd9db92e85db578029311379209348dc
- Has security context
system_u:object_r:bin_t:s0
newgrp(1) should run with CAP_SETUID
in its effective set so that it can drop privileges properly via setuid(2).
- Program to execute a command as another user or group in a configurable manner
- Provided by sudo-1.9.14-1.p3.fc39.x86_64
- Installed with mode
---s--x--x
- Has SHA256 checksum a9ddaec6f174559759cd416bfcb0b94b9947427ea80fc26d5fb77cb79a15f617
- Has security context
system_u:object_r:sudo_exec_t:s0
set by the sudo 1.10.0 module
The corresponding procedure outlined in SUID-root Binaries in Fedora Custom OS 38 is reproducible in Fedora Workstation 39.
sudo(8) no longer zeros the hard limit on the core dump file size (see commit b1deffb).
However, sudo(8) should still run with CAP_SYS_RESOURCE
to override the hard limit on the maximum number of processes available to the RUID. This ensures that the call to execve(2) after setresuid(2) does not fail with EAGAIN
.
Without CAP_SYS_RESOURCE
, I observe the following error:
1882<sudo> prlimit64(0, RLIMIT_NPROC, {rlim_cur=RLIM64_INFINITY, rlim_max=RLIM64_INFINITY}, NULL) = -1 EPERM (Operation not permitted)
Source:
sudo strace -u user -fyY sudo true
Note
The RLIMIT_NPROC
limit is ignored for processes that have CAP_SYS_RESOURCE
.
- Program to unmount file systems
- Provided by util-linux-core-2.39.2-1.fc39.x86_64
- Has SHA256 checksum c461753b7aff56db84a39dbbbe923f607d55f70dc8357b46f02a8e2c073ff0e0
- Has security context
system_u:object_r:mount_exec_t:s0
set by the mount 1.16.1 module
Yes. mount(8) and umount(8) have matching implementations and a shared dependence on libmount(3).
- Program to mount NFS file systems
- Provided by nfs-utils-1:2.6.3-1.rc3.fc39.x86_64
- Has SHA256 checksum 097c142c5b718dfc7e4ff0daf1842ec591535dc6f064fa774cbf16d349e97ab7
- Has security context
system_u:object_r:mount_exec_t:s0
set by the mount 1.16.1 module
The corresponding procedure outlined in SUID-root Binaries in Fedora Workstation 38 is reproducible in Fedora Workstation 39.
Like mount(8), mount.nfs(8) requires CAP_CHOWN
to update /run/mount/utab when an unprivileged user wants to perform a user mount of a NFSv4 file system (and therefore run mount.nfs(8) with nonzero EGID) but /run/mount/utab was created with group root
.
I started by checking the group and contents of /run/mount/utab:
user@fedora:~$ stat -c %G /run/mount/utab # c: format
root
user@fedora:~$ cat /run/mount/utab
ID=1036 SRC=/dev/sr0 TARGET=/run/media/user/Fedora-WS-Live-39-1-5 ROOT=/ OPTS=uhelper=udisks2
I then restricted the privileges on the binary of interest…
user@fedora:~$ sudo chmod u+s /usr/sbin/mount.nfs
user@fedora:~$ sudo setcap cap_chown,cap_sys_admin=ep /usr/sbin/mount.nfs
… and performed the mount:
user@fedora:~$ mount /mnt/nfs
In the syscall trace, I could see that the fchown(2) call had succeeded:
2981<mount.nfs> fchown(5</run/mount/utab.NU5hlx>, 0, 0) = 0
Source:
umount /mnt/nfs; sudo strace -u user -fyY mount /mnt/nfs
I confirmed that the NFSv4 file system had been mounted…
user@fedora:~$ findmnt /mnt/nfs
TARGET SOURCE FSTYPE OPTIONS
/mnt/nfs 192.0.2.101:/srv/nfs nfs4 ro,nosuid,nodev,noexec,relatime,vers=4.2,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.0.2.100,local_lock=none,addr=192.0.2.101
… and that /run/mount/utab had been updated accordingly:
user@fedora:~$ cat /run/mount/utab
ID=1036 SRC=/dev/sr0 TARGET=/run/media/user/Fedora-WS-Live-39-1-5 ROOT=/ OPTS=uhelper=udisks2
SRC=192.0.2.101:/srv/nfs TARGET=/mnt/nfs ROOT=/ ATTRS=vers=4.2,addr=192.0.2.101,clientaddr=192.0.2.100 OPTS=user=user
I next determined that it is not strictly necessary for /usr/sbin/mount.nfs to carry CAP_CHOWN
when /usr/bin/mount already carries CAP_CHOWN
because mount.nfs(8) can delegate the update of /run/mount/utab to mount(8):
user@fedora:~$ umount /mnt/nfs
user@fedora:~$ sudo setcap cap_sys_admin=ep /usr/sbin/mount.nfs
user@fedora:~$ sudo chmod u+s /usr/bin/mount
user@fedora:~$ sudo setcap cap_chown,cap_sys_admin=ep /usr/bin/mount
user@fedora:~$ mount /mnt/nfs
3155<mount.nfs> write(5</run/mount/utab.pF0gLL>, "ID=1036 SRC=/dev/sr0 TARGET=/run"..., 221) = 221
...
3155<mount.nfs> fchown(5</run/mount/utab.pF0gLL>, 0, 0) = -1 EPERM (Operation not permitted)
...
3155<mount.nfs> unlink("/run/mount/utab.pF0gLL") = 0
...
3155<mount.nfs> +++ exited with 0 +++
...
3153<mount> write(4</run/mount/utab.mTa82n>, "ID=1036 SRC=/dev/sr0 TARGET=/run"..., 160) = 160
...
3153<mount> fchown(4</run/mount/utab.mTa82n>, 0, 0) = 0
...
3153<mount> rename("/run/mount/utab.mTa82n", "/run/mount/utab") = 0
Source:
umount /mnt/nfs; sudo strace -u user -fyY mount /mnt/nfs
after omitting select syscalls
Once again, I had mounted the file system successfully:
user@fedora:~$ findmnt /mnt/nfs
TARGET SOURCE FSTYPE OPTIONS
/mnt/nfs 192.0.2.101:/srv/nfs nfs4 ro,nosuid,nodev,noexec,relatime,vers=4.2,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.0.2.100,local_lock=none,addr=192.0.2.101
However, I noticed that mount(8) wrote fewer bytes than mount.nfs(8) when updating /run/mount/utab. Indeed, mount(8) did not record the user-space attributes of the mount:
user@fedora:~$ cat /run/mount/utab
ID=1036 SRC=/dev/sr0 TARGET=/run/media/user/Fedora-WS-Live-39-1-5 ROOT=/ OPTS=uhelper=udisks2
SRC=192.0.2.101:/srv/nfs TARGET=/mnt/nfs ROOT=/ OPTS=user=user
If you run user-space applications that consume NFSv4 attributes in /run/mount/utab, then confer both CAP_CHOWN
and CAP_SYS_ADMIN
to /usr/sbin/mount.nfs. However, be advised that the libmount(3) documentation discourages the use of attributes.
- Program to validate or remove the default timestamp (for cached authentication results)
- Provided by pam-1.5.3-2.fc39.x86_64
- Has SHA256 checksum 42660967c8ee116c1ea0c582eee450193ee2f858af50f59bccfea23ae57f4318
- Has security context
system_u:object_r:pam_timestamp_exec_t:s0
set by the authlogin 2.5.1 module
The corresponding procedure outlined in SUID-root Binaries in Fedora Custom OS 38 is reproducible in Fedora Workstation 39.
sudo(8) now uses a pseudoterminal by default (see commit 894daa8), so it’s no longer possible to obtain accurate traces of pam_timestamp_check(8) using strace(1). This is because, under sudo(8), pam_timestamp_check(8) will manipulate the authentication timestamps for the pseudoterminal. To obtain accurate traces, it’s therefore necessary to disable this feature temporarily like so:
user@fedora:~$ sudo visudo /etc/sudoers.d/no_pty
Defaults !use_pty
sudo(8) will now run without creating a pseudoterminal for the given command.
When done tracing, delete the custom policy:
user@fedora:~$ sudo rm /etc/sudoers.d/no_pty
- Program to help other programs interface with PAM
- Provided by usermode-1.114-8.fc39.x86_64
- Installed with mode
-rws--x--x
- Has SHA256 checksum 9a28e9bd86cf949d8596f169cc61a6858df777a337ae6111be63e6594ae98a82
- Has security context
system_u:object_r:userhelper_exec_t:s0
set by the userhelper 1.8.1 module
This program no longer supports the Anaconda installer:
liveuser@localhost-live:~$ cat /usr/share/applications/anaconda.desktop | grep '^Exec'
Exec=liveinst
liveuser@localhost-live:~$ command -v liveinst
/usr/bin/liveinst
liveuser@localhost-live:~$ file /usr/bin/liveinst
/usr/bin/liveinst: Bourne-Again shell script, ASCII text executable
The file at /usr/bin/liveinst is the same shell script that consolehelper(8) and userhelper(8) conspired to run at /usr/sbin/liveinst in Fedora Workstation 38.
How does Anaconda run with privileges without userhelper(8)? The liveinst script now uses pkexec(1), another SUID-root binary I covered in SUID-root Binaries in Fedora Custom OS 38, to rerun itself as root:
2831<liveinst> execve("/usr/bin/pkexec", ["pkexec", "/usr/bin/liveinst"], 0x55aa520eccb0 /* 26 vars */) = 0
Source:
sudo strace -u liveuser -fyY -E DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS}" gtk-launch anaconda
, where-E
is short for--env
I identified 27 SUID-root binaries on an installation of Fedora Workstation 39 to a VirtIO disk.
In SUID-root Binaries in Fedora Custom OS 38 and SUID-root Binaries in Fedora Workstation 38, I had already analyzed 25 of these binaries.
Of these, one program (/usr/sbin/userhelper) no longer required privileges for its reference use case.
I was able to substitute the SUID bit using file capabilities on all the new binaries:
- /usr/bin/chfn
- /usr/bin/chsh
The new programs of interest ran unconfined by SELinux.
In total, 10/27 SUID-root binaries required SUID-root execution to succeed. These were:
- /usr/bin/mount
- /usr/bin/passwd
- /usr/bin/pkexec
- /usr/bin/su
- /usr/bin/sudo
- /usr/bin/umount
- /usr/lib/polkit-1/polkit-agent-helper-1
- /usr/libexec/dbus-1/dbus-daemon-launch-helper
- /usr/sbin/mount.nfs
- /usr/sbin/pam_timestamp_check
To get a high-level idea of the privileges needed by the SUID-root binaries that come with Fedora Workstation 39, I tallied the file capabilities that I ended up setting to enable the reference use cases:
CAP_CHOWN ┤████████████████ 8
CAP_DAC_OVERRIDE ┤██████████████ 7
CAP_DAC_READ_SEARCH ┤████████ 4
CAP_FOWNER ┤████████ 4
CAP_SETGID ┤████████████ 6
CAP_SETUID ┤██████████████████ 9
CAP_NET_ADMIN ┤██ 1
CAP_SYS_ADMIN ┤████████████ 6
CAP_SYS_RESOURCE ┤██ 1
CAP_AUDIT_WRITE ┤████████████████████ 10
API Application programming interface
BCC BPF Compiler Collection
BPF Berkeley Packet Filter
CC Creative Commons
DAC Discretionary access control
EGID Effective GID
EUID Effective UID
FUSE Filesystem in Userspace
GCC GNU Compiler Collection
GECOS General Comprehensive Operating System
GID Group ID
GNU GNU’s Not Unix
GPG GNU Privacy Guard
GRUB Grand Unified Bootloader
ID Identifier
KVM Kernel-based Virtual Machine
NFS Network File System
OS Operating system
PAM Pluggable Authentication Modules
QEMU Quick Emulator
RUID Real UID
SELinux Security-Enhanced Linux
SHA Secure Hash Algorithm
SPICE Simple Protocol for Independent Computing Environments
SSH Secure Shell
SUID Set-UID
UID User ID
USB Universal Serial Bus
VM Virtual machine
- Program to display and change user password expiry information
- Provided by shadow-utils-2:4.14.0-1.fc39.x86_64
- Has SHA256 checksum a3173997e1b202950a54860cde97530ab2db135b1ba828beb33a4d95cb112774
- Has security context
system_u:object_r:passwd_exec_t:s0
set by the usermanage 1.19.0 module
- Program to mount and unmount FUSE v2 file systems
- Provided by fuse-2.9.9-17.fc39.x86_64
- Has SHA256 checksum 1016a98425a6289e63e24d0670cc63f656054b7f1d80bcb172a408d4cbc85402
- Has security context
system_u:object_r:fusermount_exec_t:s0
set by the mount 1.16.1 module
- Program to mount and unmount Gluster file systems
- Provided by glusterfs-fuse-11.0-5.fc39.x86_64
- Has SHA256 checksum 632efb9ed73871ab16c511c7a610ca268689a1d47833d355b81cfa8114131b72
- Has security context
system_u:object_r:bin_t:s0
- Program to mount and unmount FUSE v3 file systems
- Provided by fuse3-3.16.1-1.fc39.x86_64
- Has SHA256 checksum 904a8f9637c6998ed285cd8289fe267b07d215e44caec5115d03bb2254506dbe
- Has security context
system_u:object_r:fusermount_exec_t:s0
set by the mount 1.16.1 module
- Program to update authentication tokens using PAM
- Provided by passwd-0.80-15.fc39.x86_64
- Has SHA256 checksum 9f369ef1b405aca3a0b7a8b7badc53c53bc73730368bc2e9134ffb4b6262c281
- Has security context
system_u:object_r:passwd_exec_t:s0
set by the usermanage 1.19.0 module
- Program to execute a command as another user following authorization by Polkit
- Provided by polkit-123-1.fc39.x86_64
- Has SHA256 checksum f1b4b019b8f1cbc28d5c204cab20356a82e7b890c3b536f121a3332278834135
- Has security context
system_u:object_r:bin_t:s0
- Program to run a new shell or a command within a new shell as another user or group
- Provided by util-linux-2.39.2-1.fc39.x86_64
- Has SHA256 checksum e1073223ad40d277ad2717b4d60b556d30403e07e51eb4389a9858abd97eaeb9
- Has security context
system_u:object_r:su_exec_t:s0
set by the su 1.12.0 module
- Program to run the VMware user process
- Provided by open-vm-tools-desktop-12.3.0-1.fc39.x86_64
- Has SHA256 checksum 98271fbf0c82274fbf3b19735b390d845a71de72c39080e09ebf619ea8cf6646
- Has security context
system_u:object_r:vmtools_helper_exec_t:s0
set by the vmtools 1.0.0 module
- Polkit agent helper to re-authenticate a user
- Provided by polkit-123-1.fc39.x86_64
- Has SHA256 checksum 73f2e3da9c59c1dcb43ea4f1e139bf241c2482347e216f04cba74dcbdff87f92
- Has security context
system_u:object_r:policykit_auth_exec_t:s0
set by the policykit 1.3.0 module
- Program to help dbus-daemon(1) start a D-Bus service on demand as another user
- Provided by dbus-daemon-1:1.14.10-1.fc39.x86_64
- Installed with group
dbus
and mode-rwsr-x---
- Has SHA256 checksum 6f580a72653cad5d73511c6a5d5f67cefdda336c6533f13c5d2dbafc6d998511
- Has security context
system_u:object_r:dbusd_exec_t:s0
set by the dbus 1.19.0 module
- OpenSSH helper for host-based authentication
- Provided by openssh-9.3p1-9.fc39.x86_64
- Installed with mode
-r-sr-xr-x
- Has SHA256 checksum f48922594a181ffcaec1b6253a9cc5dfd5d36c1f366c7c4fc5f2bb8f76c882bb
- Has security context
system_u:object_r:ssh_keysign_exec_t:s0
set by the ssh 2.4.2 module
- SPICE helper for USB redirection
- Provided by spice-glib-0.42-3.fc39.x86_64
- Has SHA256 checksum e07f62f7d5eeca9f914bfda59f629d0770da0b12f6df1c4874f4752867af90e5
- Has security context
system_u:object_r:bin_t:s0
- Program to run Xorg(1), the reference implementation of the X Window System, possibly as root
- Provided by xorg-x11-server-Xorg-1.20.14-24.fc39.x86_64
- Has SHA256 checksum 3ac52cf5c6dda6d1050fb93aee8cf22153b83fb524828da46c0e9c88f719215b
- Has security context
system_u:object_r:xserver_exec_t:s0
set by the xserver 3.9.4 module
- Program to collect system monitoring data, e.g., resource usage
- Provided by libgtop2-2.41.1-2.fc39.x86_64
- Has SHA256 checksum 3ee6dd4e2a6389e5af140df8cdfe9d9c77b82fdab36b4674ac0bed875d3022be
- Has security context
system_u:object_r:bin_t:s0
- QEMU helper to let unprivileged users connect a QEMU VM to a bridged network interface
- Provided by qemu-common-2:8.1.0-1.fc39.x86_64
- Has SHA256 checksum a91eff46f1264c97661777c82aeb7e701a509c0d6fb1c2256dbd5d2c0d941456
- Has security context
system_u:object_r:virt_bridgehelper_exec_t:s0
set by the virt 1.5.0 module
- Program to set a bootflag in the GRUB environment block
- Provided by grub2-tools-minimal-1:2.06-100.fc39.x86_64
- Has SHA256 checksum dfa34c39cc4b3e8c0cb88fdca5f037db878ed72ea5df2f4e81eb4dfb7b708b1f
- Has security context
system_u:object_r:bootloader_exec_t:s0
set by the bootloader 1.14.0 module
- Helper for the
pam_unix
PAM module that verifies the running user’s password - Provided by pam-1.5.3-2.fc39.x86_64
- Has SHA256 checksum b78d6c1b0c0aaa0f720922445a4f6f66db8d0ed44f73890c8c31e2ebb8806ece
- Has security context
system_u:object_r:chkpwd_exec_t:s0
set by the authlogin 2.5.1 module
Please connect with me if you have constructive comments about this article, especially if you have a use case I didn’t cover or are unable to reproduce a procedure in an identical environment.
Fedora Linux 39 reached end of life on November 26, 2024. I am therefore no longer maintaining this report actively. I will detail corrections in SUID-root Binaries in Fedora Workstation 40 or a later report.
The author acknowledges private funding by one anonymous sponsor.
All original copyrightable content in this document is marked with CC0 1.0 Universal.
- Change Fedora Server 38 references to refer to Fedora Custom OS 38
- Expand OS abbreviation in Appendix A
- State discontinuation of maintenance of report
Indicate CAP_SETUID
for programs that drop privileges via setuid(2) (/usr/bin/mount, /usr/bin/newgrp and /usr/bin/umount)
- Document additional supporting utilities and development headers
- Expand BCC and GCC abbreviations in Appendix A
- Arrange sections according to order in output of find(1)
- Use correct manual page section for strace(1)
- Correct assertion about
CAP_SYS_RESOURCE
with respect to gpasswd(1) - Discuss relevance of
CAP_CHOWN
to mount.nfs(8) - Count and list binaries that must run SUID-root to succeed
- Update capabilities histogram
- Expand BPF, DAC, EGID and EUID abbreviations in Appendix A
- Comment on presence of
CAP_WAKE_ALARM
in the inheritable set of login shells - Support assertion that no securebits flags had been set in the working environments
- Discuss relevance of
CAP_SYS_RESOURCE
to gpasswd(1) - Correct assertion about relevance of
CAP_SYS_RESOURCE
to sudo(8) - Update Fedora Linux 39 end-of-life date
- Add missing subheader
- Add hyperlinks to Linux man-pages
- Use unused hyperlinks
- Fix hyperlink to strace RPM
- Ensure alphabetic sort order for all hyperlinks
Revise executive statement
Initial revision