From bc325cca6d08dec326542e4a388daa4421b4a80c Mon Sep 17 00:00:00 2001 From: qoijjj <129108030+qoijjj@users.noreply.github.com> Date: Sat, 29 Jun 2024 10:34:06 -0700 Subject: [PATCH] feat: replace SUID root with capabilities where possible (#303) --- README.md | 1 + config/files/usr/bin/setcapsforunsuidbinaries | 24 ++++++ .../system/setcapsforunsuidbinaries.service | 13 +++ config/scripts/removesuid.sh | 79 +++++++++++++++++++ recipes/common/common-scripts.yml | 3 +- 5 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 config/files/usr/bin/setcapsforunsuidbinaries create mode 100644 config/files/usr/lib/systemd/system/setcapsforunsuidbinaries.service create mode 100644 config/scripts/removesuid.sh diff --git a/README.md b/README.md index 2ec7d50..becd33b 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Hardening applied: - Disable KDE GHNS by default [why?](https://blog.davidedmundson.co.uk/blog/kde-store-content/) - Use HTTPS for all rpm mirrors - Set all default container policies to `reject`, `signedBy`, or `sigstoreSigned` +- Remove SUID-root from [numerous binaries](https://github.com/secureblue/secureblue/blob/live/config/scripts/removesuid.sh) and replace functionality [using capabilities](https://github.com/secureblue/secureblue/blob/staging/config/files/usr/bin/setcapsforunsuidbinaries) - (Non-userns variants) Disabling unprivileged user namespaces - (Non-userns variants) Replacing bubblewrap with bubblewrap-suid so flatpak can be used without unprivileged user namespaces diff --git a/config/files/usr/bin/setcapsforunsuidbinaries b/config/files/usr/bin/setcapsforunsuidbinaries new file mode 100644 index 0000000..95849e5 --- /dev/null +++ b/config/files/usr/bin/setcapsforunsuidbinaries @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +set_caps_if_present() { + local caps="$1" + local binary_path="$2" + if [ -f "$binary_path" ]; then + mount --bind -o rw "$binary_path" "$binary_path" + + echo "Setting caps $caps on $binary_path" + setcap "$caps" "$binary_path" + echo "Set caps $caps on $binary_path" + + umount "$binary_path" + fi +} + +set_caps_if_present "cap_dac_read_search,cap_audit_write=ep" "/usr/bin/chage" +set_caps_if_present "cap_chown,cap_dac_override,cap_fowner,cap_audit_write=ep" "/usr/bin/chsh" +set_caps_if_present "cap_chown,cap_dac_override,cap_fowner,cap_audit_write=ep" "/usr/bin/chfn" +set_caps_if_present "cap_dac_read_search=ep" "/usr/libexec/openssh/ssh-keysign" +set_caps_if_present "cap_sys_admin=ep" "/usr/bin/fusermount" +set_caps_if_present "cap_sys_admin=ep" "/usr/bin/fusermount3" +set_caps_if_present "cap_sys_admin=ep" "/usr/bin/fusermount-glusterfs" +set_caps_if_present "cap_dac_read_search,cap_audit_write=ep" "/usr/sbin/unix_chkpwd" diff --git a/config/files/usr/lib/systemd/system/setcapsforunsuidbinaries.service b/config/files/usr/lib/systemd/system/setcapsforunsuidbinaries.service new file mode 100644 index 0000000..1203724 --- /dev/null +++ b/config/files/usr/lib/systemd/system/setcapsforunsuidbinaries.service @@ -0,0 +1,13 @@ +[Unit] +Description=Set caps for suid-removed binaries +After=local-fs.target sysinit.target +DefaultDependencies=no +Before=graphical-session-pre.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/setcapsforunsuidbinaries +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/config/scripts/removesuid.sh b/config/scripts/removesuid.sh new file mode 100644 index 0000000..08b6993 --- /dev/null +++ b/config/scripts/removesuid.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +# Tell build process to exit if there are any errors. +set -oue pipefail + +# Reference: https://gist.github.com/ok-ryoko/1ff42a805d496cb1ca22e5cdf6ddefb0#usrbinchage + +whitelist=( + # Needed for flatpak on no-userns images + "/usr/bin/bwrap" + # Requires cap_setuid if the suid bit is removed + "/usr/bin/gpasswd" + # "In effect, when the SUID bit is unset on /usr/bin/mount, mount(8) will never drop permissions. If /usr/bin/mount were to have a" + # "nonempty permitted capability set and its effective capability bit were set, then mount(8) would never have its effective " + # "capability set cleared during execution, potentially allowing unprivileged users to perform actions they shouldn’t be able to perform" + # https://gist.github.com/ok-ryoko/1ff42a805d496cb1ca22e5cdf6ddefb0#can-we-replace-the-suid-bit-with-zero-or-more-file-capabilities-4 + "/usr/bin/mount" + # Required for nvidia images + "/usr/bin/nvidia-modprobe" + # https://gist.github.com/ok-ryoko/1ff42a805d496cb1ca22e5cdf6ddefb0#can-we-replace-the-suid-bit-with-zero-or-more-file-capabilities + "/usr/bin/passwd" + # https://gist.github.com/ok-ryoko/1ff42a805d496cb1ca22e5cdf6ddefb0#why-does-this-binary-need-to-be-suid-root-9 + "/usr/bin/pkexec" + # https://gist.github.com/ok-ryoko/1ff42a805d496cb1ca22e5cdf6ddefb0#can-we-replace-the-suid-bit-with-zero-or-more-file-capabilities-6 + "/usr/bin/su" + # https://gist.github.com/ok-ryoko/1ff42a805d496cb1ca22e5cdf6ddefb0#can-we-replace-the-suid-bit-with-zero-or-more-file-capabilities-6 + "/usr/bin/sudo" + # See /usr/bin/mount + "/usr/bin/umount" + # https://gitlab.freedesktop.org/polkit/polkit/-/issues/168 + "/usr/lib/polkit-1/polkit-agent-helper-1" + # https://github.com/secureblue/secureblue/issues/119 + "/usr/lib64/libhardened_malloc-light.so" + "/usr/lib64/libhardened_malloc-pkey.so" + "/usr/lib64/libhardened_malloc.so" + # Required for chrome suid sandbox on no-userns images + "/usr/lib64/chromium-browser/chrome-sandbox" + # https://github.com/secureblue/secureblue/issues/119 + "/usr/lib64/glibc-hwcaps/x86-64/libhardened_malloc-light.so" + "/usr/lib64/glibc-hwcaps/x86-64/libhardened_malloc-pkey.so" + "/usr/lib64/glibc-hwcaps/x86-64/libhardened_malloc.so" + "/usr/lib64/glibc-hwcaps/x86-64-v2/libhardened_malloc-light.so" + "/usr/lib64/glibc-hwcaps/x86-64-v2/libhardened_malloc-pkey.so" + "/usr/lib64/glibc-hwcaps/x86-64-v2/libhardened_malloc.so" + "/usr/lib64/glibc-hwcaps/x86-64-v3/libhardened_malloc-light.so" + "/usr/lib64/glibc-hwcaps/x86-64-v3/libhardened_malloc-pkey.so" + "/usr/lib64/glibc-hwcaps/x86-64-v3/libhardened_malloc.so" + "/usr/lib64/glibc-hwcaps/x86-64-v4/libhardened_malloc-light.so" + "/usr/lib64/glibc-hwcaps/x86-64-v4/libhardened_malloc-pkey.so" + "/usr/lib64/glibc-hwcaps/x86-64-v4/libhardened_malloc.so" + # Requires cap_setgid,cap_setuid if the SUID bit is removed + "/usr/sbin/grub2-set-bootflag" + # See /usr/bin/mount + "/usr/sbin/mount.nfs" + # https://gist.github.com/ok-ryoko/1ff42a805d496cb1ca22e5cdf6ddefb0#why-does-this-binary-need-to-be-suid-root-6 + "/usr/sbin/pam_timestamp_check" +) + + +is_in_whitelist() { + local binary="$1" + for allowed_binary in "${whitelist[@]}"; do + if [ "$binary" = "$allowed_binary" ]; then + return 0 + fi + done + return 1 +} + +find /usr -type f -perm /4000 | + while IFS= read -r binary; do + if ! is_in_whitelist "$binary"; then + echo "Removing SUID bit from $binary" + chmod u-s "$binary" + echo "Removed SUID bit from $binary" + fi + done + +systemctl enable setcapsforunsuidbinaries.service diff --git a/recipes/common/common-scripts.yml b/recipes/common/common-scripts.yml index 01460f7..f031e64 100644 --- a/recipes/common/common-scripts.yml +++ b/recipes/common/common-scripts.yml @@ -6,4 +6,5 @@ scripts: - homebrewanalyticsoptout.sh - hardencontainerpolicy.sh - httpsmirrors.sh - - createmissingdirectories.sh \ No newline at end of file + - createmissingdirectories.sh + - removesuid.sh \ No newline at end of file