1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
https://github.com/util-linux/util-linux/commit/286dd3ff41526b582ef48830de239dffbaa61f90
From 286dd3ff41526b582ef48830de239dffbaa61f90 Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
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 <kzak@redhat.com>
(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;
|