https://github.com/util-linux/util-linux/commit/286dd3ff41526b582ef48830de239dffbaa61f90 From 286dd3ff41526b582ef48830de239dffbaa61f90 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 3 Sep 2026 12:17:14 +0200 Subject: [PATCH] nsenter: close cgroup.procs fd after join to prevent authority leak [CVE-2026-78408] The --join-cgroup option opens the target's cgroup.procs while running as root and writes nsenter's own PID to migrate itself. The descriptor was left open across subsequent namespace transitions, credential drops (setgroups/setgid/setuid) and execve(). The kernel performs cgroup migration permission checks using the credentials captured at open time (file->f_cred). An open cgroup.procs descriptor therefore carries the opener's migration authority regardless of later privilege changes. A program executed inside the target namespace inherits root's cgroup migration capability even when running as an unprivileged user with no capabilities. Fix this by: - closing the temporary /proc/PID/cgroup fd after reading the path - adding O_CLOEXEC to the cgroup.procs open as defense in depth - closing cgroup_procs_fd immediately after the self-migration write - initializing the temporary cgroup fd to -1 instead of 0 to avoid accidentally closing stdin via open_target_fd() The descriptor has no legitimate use after the single migration write. Introduced-by: b40650b71a74 ("nsenter: add option -c to join the cgroup of target process") References: b0cf1cf0d255 ("nsenter: close cgroup.procs fd after join to prevent authority leak") Signed-off-by: Karel Zak (cherry picked from commit afe067c979b9ba2cbe856f7c6411210120ea62aa) --- sys-utils/nsenter.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index 62ef366d430..f449c65d2b4 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -466,7 +466,7 @@ static int get_ns_ino(const char *path, ino_t *ino) static void open_cgroup_procs(void) { char *buf = NULL, *path = NULL, *p; - int cgroup_fd = 0; + int cgroup_fd = -1; char fdpath[PATH_MAX]; open_target_fd(&cgroup_fd, "cgroup", optarg); @@ -474,6 +474,8 @@ static void open_cgroup_procs(void) if (read_all_alloc(cgroup_fd, &buf) < 1) err(EXIT_FAILURE, _("failed to get cgroup path")); + close(cgroup_fd); + p = strtok(buf, "\n"); if (p) path = strrchr(p, ':'); @@ -483,7 +485,7 @@ static void open_cgroup_procs(void) snprintf(fdpath, sizeof(fdpath), _PATH_SYS_CGROUP "/%s/cgroup.procs", path); - if ((cgroup_procs_fd = open(fdpath, O_WRONLY | O_APPEND)) < 0) + if ((cgroup_procs_fd = open(fdpath, O_WRONLY | O_APPEND | O_CLOEXEC)) < 0) err(EXIT_FAILURE, _("failed to open cgroup.procs")); free(buf); @@ -923,8 +925,11 @@ int main(int argc, char *argv[]) } // Join into the target cgroup - if (cgroup_procs_fd >= 0) + if (cgroup_procs_fd >= 0) { join_into_cgroup(); + close(cgroup_procs_fd); + cgroup_procs_fd = -1; + } if (uid_gid_fd >= 0) { struct stat st;