From: Josef Bacik <josef@toxicpanda.com>
To: Christian Brauner <brauner@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>,
linux-fsdevel@vger.kernel.org,
Alexander Viro <viro@zeniv.linux.org.uk>, Jan Kara <jack@suse.cz>,
Aleksa Sarai <cyphar@cyphar.com>
Subject: Re: [PATCH RFC 0/6] proc: restrict overmounting of ephemeral entities
Date: Wed, 7 Aug 2024 10:33:39 -0400 [thread overview]
Message-ID: <20240807143339.GD242945@perftesting> (raw)
In-Reply-To: <20240806-work-procfs-v1-0-fb04e1d09f0c@kernel.org>
On Tue, Aug 06, 2024 at 06:02:26PM +0200, Christian Brauner wrote:
> (Preface because I've been panick-approached by people at conference
> when we discussed this before: overmounting any global procfs files
> such as /proc/status remains unaffected and is an existing and
> supported use-case.)
>
> It is currently possible to mount on top of various ephemeral entities
> in procfs. This specifically includes magic links. To recap, magic links
> are links of the form /proc/<pid>/fd/<nr>. They serve as references to
> a target file and during path lookup they cause a jump to the target
> path. Such magic links disappear if the corresponding file descriptor is
> closed.
>
> Currently it is possible to overmount such magic links:
>
> int fd = open("/mnt/foo", O_RDONLY);
> sprintf(path, "/proc/%d/fd/%d", getpid(), fd);
> int fd2 = openat(AT_FDCWD, path, O_PATH | O_NOFOLLOW);
> mount("/mnt/bar", path, "", MS_BIND, 0);
>
> Arguably, this is nonsensical and is mostly interesting for an attacker
> that wants to somehow trick a process into e.g., reopening something
> that they didn't intend to reopen or to hide a malicious file
> descriptor.
>
> But also it risks leaking mounts for long-running processes. When
> overmounting a magic link like above, the mount will not be detached
> when the file descriptor is closed. Only the target mountpoint will
> disappear. Which has the consequence of making it impossible to unmount
> that mount afterwards. So the mount will stick around until the process
> exits and the /proc/<pid>/ directory is cleaned up during
> proc_flush_pid() when the dentries are pruned and invalidated.
>
> That in turn means it's possible for a program to accidentally leak
> mounts and it's also possible to make a task leak mounts without it's
> knowledge if the attacker just keeps overmounting things under
> /proc/<pid>/fd/<nr>.
>
> I think it's wrong to try and fix this by us starting to play games with
> close() or somewhere else to undo these mounts when the file descriptor
> is closed. The fact that we allow overmounting of such magic links is
> simply a bug and one that we need to fix.
>
> Similar things can be said about entries under fdinfo/ and map_files/ so
> those are restricted as well.
>
> I have a further more aggressive patch that gets out the big hammer and
> makes everything under /proc/<pid>/*, as well as immediate symlinks such
> as /proc/self, /proc/thread-self, /proc/mounts, /proc/net that point
> into /proc/<pid>/ not overmountable. Imho, all of this should be blocked
> if we can get away with it. It's only useful to hide exploits such as in [1].
>
> And again, overmounting of any global procfs files remains unaffected
> and is an existing and supported use-case.
>
> Link: https://righteousit.com/2024/07/24/hiding-linux-processes-with-bind-mounts [1]
>
> // Note that repro uses the traditional way of just mounting over
> // /proc/<pid>/fd/<nr>. This could also all be achieved just based on
> // file descriptors using move_mount(). So /proc/<pid>/fd/<nr> isn't the
> // only entry vector here. It's also possible to e.g., mount directly
> // onto /proc/<pid>/map_files/* without going over /proc/<pid>/fd/<nr>.
> int main(int argc, char *argv[])
> {
> char path[PATH_MAX];
>
> creat("/mnt/foo", 0777);
> creat("/mnt/bar", 0777);
>
> /*
> * For illustration use a bunch of file descriptors in the upper
> * range that are unused.
> */
> for (int i = 10000; i >= 256; i--) {
> printf("I'm: /proc/%d/\n", getpid());
>
> int fd2 = open("/mnt/foo", O_RDONLY);
> if (fd2 < 0) {
> printf("%m - Failed to open\n");
> _exit(1);
> }
>
> int newfd = dup2(fd2, i);
> if (newfd < 0) {
> printf("%m - Failed to dup\n");
> _exit(1);
> }
> close(fd2);
>
> sprintf(path, "/proc/%d/fd/%d", getpid(), newfd);
> int fd = openat(AT_FDCWD, path, O_PATH | O_NOFOLLOW);
> if (fd < 0) {
> printf("%m - Failed to open\n");
> _exit(3);
> }
>
> sprintf(path, "/proc/%d/fd/%d", getpid(), fd);
> printf("Mounting on top of %s\n", path);
> if (mount("/mnt/bar", path, "", MS_BIND, 0)) {
> printf("%m - Failed to mount\n");
> _exit(4);
> }
>
> close(newfd);
> close(fd2);
> }
>
> /*
> * Give some time to look at things. The mounts now linger until
> * the process exits.
> */
> sleep(10000);
> _exit(0);
> }
>
> Co-developed-by: Aleksa Sarai <cyphar@cyphar.com>
> Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
> Signed-off-by: Christian Brauner <brauner@kernel.org>
I'm always down to restrict /proc, you can add
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Thanks,
Josef
next prev parent reply other threads:[~2024-08-07 14:33 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-08-06 16:02 [PATCH RFC 0/6] proc: restrict overmounting of ephemeral entities Christian Brauner
2024-08-06 16:02 ` [PATCH RFC 1/6] proc: proc_readfd() -> proc_fd_iterate_shared() Christian Brauner
2024-08-06 17:06 ` Linus Torvalds
2024-08-06 16:02 ` [PATCH RFC 2/6] proc: proc_readfdinfo() -> proc_fdinfo_iterate_shared() Christian Brauner
2024-08-06 16:02 ` [PATCH RFC 3/6] proc: add proc_splice_unmountable() Christian Brauner
2024-08-06 16:02 ` [PATCH RFC 4/6] proc: block mounting on top of /proc/<pid>/map_files/* Christian Brauner
2024-08-06 16:02 ` [PATCH RFC 5/6] proc: block mounting on top of /proc/<pid>/fd/* Christian Brauner
2024-08-06 16:02 ` [PATCH RFC 6/6] proc: block mounting on top of /proc/<pid>/fdinfo/* Christian Brauner
2024-08-07 14:33 ` Josef Bacik [this message]
2025-09-30 8:53 ` [PATCH RFC 0/6] proc: restrict overmounting of ephemeral entities Petr Vorel
2025-09-30 15:51 ` Aleksa Sarai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240807143339.GD242945@perftesting \
--to=josef@toxicpanda.com \
--cc=brauner@kernel.org \
--cc=cyphar@cyphar.com \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=torvalds@linux-foundation.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox