Skip to content

Instantly share code, notes, and snippets.

@ok-ryoko
Last active November 28, 2024 19:37
Show Gist options
  • Save ok-ryoko/df15bfb7b3032f3f093f0865f3efb39b to your computer and use it in GitHub Desktop.
Save ok-ryoko/df15bfb7b3032f3f093f0865f3efb39b to your computer and use it in GitHub Desktop.
SUID-root Binaries in Fedora Workstation 39

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.

SUID-root Binaries in Fedora Workstation 39

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.

Table of contents

Characterization of environments

Installed to a virtual hard disk drive

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:

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.

Live in memory

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.

Conventions for presenting results

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.

The SUID-root binaries in detail

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.

/usr/bin/chfn

Overview

  • 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

Why does this binary need to be SUID-root?

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).

Can we replace the SUID bit with zero or more file capabilities?

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

Does this program drop privileges? If so, when?

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).

What role does SELinux play in constraining the privilege of the program?

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.

Comments

If unprivileged users should not be able to change their finger information, then consider unsetting the SUID bit on /usr/bin/chfn.

Source code references

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).

/usr/bin/chsh

Overview

  • 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

Can we generalize the findings for /usr/bin/chfn to this binary?

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

Comments

If unprivileged users should not be able to change their login shell, then consider unsetting the SUID bit on /usr/bin/chsh.

Source code references

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.

/usr/bin/gpasswd

Overview

  • 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

What have I learned about this binary since prior work?

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.

/usr/bin/mount

Overview

  • 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

How has this binary changed since Fedora Workstation 38 1.6?

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

What have I learned about this binary since prior work?

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.

/usr/bin/newgrp

Overview

  • 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

What have I learned about this binary since prior work?

newgrp(1) should run with CAP_SETUID in its effective set so that it can drop privileges properly via setuid(2).

/usr/bin/sudo

Overview

  • 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

How has this binary changed since Fedora Workstation 38 1.6?

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.

/usr/bin/umount

Overview

  • 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

Can the findings for /usr/bin/mount be generalized to this binary?

Yes. mount(8) and umount(8) have matching implementations and a shared dependence on libmount(3).

/usr/sbin/mount.nfs

Overview

  • 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

What have I learned about this binary since prior work?

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.

/usr/sbin/pam_timestamp_check

Overview

  • 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

How has this binary changed since Fedora Workstation 38 1.6?

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

/usr/sbin/userhelper

Overview

  • 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

How has this binary changed since Fedora Workstation 38 1.6?

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

The findings at a glance

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

Supporting information

Appendix A: List of abbreviations

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

Appendix B: Metadata for the remaining binaries

/usr/bin/chage

  • 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

/usr/bin/fusermount

  • 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

/usr/bin/fusermount-glusterfs

  • 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

/usr/bin/fusermount3

  • 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

/usr/bin/passwd

  • 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

/usr/bin/pkexec

  • 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

/usr/bin/su

  • 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

/usr/bin/vmware-user-suid-wrapper

/usr/lib/polkit-1/polkit-agent-helper-1

  • 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

/usr/libexec/dbus-1/dbus-daemon-launch-helper

  • 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

/usr/libexec/openssh/ssh-keysign

  • 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

/usr/libexec/spice-gtk-x86_64/spice-client-glib-usb-acl-helper

/usr/libexec/Xorg.wrap

/usr/libexec/libgtop_server2

  • 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

/usr/libexec/qemu-bridge-helper

  • 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

/usr/sbin/grub2-set-bootflag

  • 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

/usr/sbin/unix_chkpwd

  • 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

Feedback and support

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.

Funding sources

The author acknowledges private funding by one anonymous sponsor.

Licensing

All original copyrightable content in this document is marked with CC0 1.0 Universal.

Revision history

2024-11-28.1

  • Change Fedora Server 38 references to refer to Fedora Custom OS 38
  • Expand OS abbreviation in Appendix A
  • State discontinuation of maintenance of report

2024-11-24.1

Indicate CAP_SETUID for programs that drop privileges via setuid(2) (/usr/bin/mount, /usr/bin/newgrp and /usr/bin/umount)

2024-11-20.3

  • Document additional supporting utilities and development headers
  • Expand BCC and GCC abbreviations in Appendix A

2024-11-20.2

  • Arrange sections according to order in output of find(1)
  • Use correct manual page section for strace(1)

2024-11-20.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

2024-11-18.1

  • 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

2024-02-12.1

Revise executive statement

2024-02-11.1

Initial revision

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment