|
Message-ID: <CAHmME9oHBtR4fBBUY8E_Oi7av-=OjOGkSNhQuMJMHhafCjazBw@mail.gmail.com> Date: Mon, 1 Feb 2021 18:47:17 +0100 From: "Jason A. Donenfeld" <Jason@...c4.com> To: Kernel Hardening <kernel-hardening@...ts.openwall.com>, Andy Lutomirski <luto@...capital.net> Cc: LKML <linux-kernel@...r.kernel.org>, Jann Horn <jann@...jh.net>, Christian Brauner <christian.brauner@...onical.com> Subject: forkat(int pidfd), execveat(int pidfd), other awful things? Hi Andy & others, I was reversing some NT stuff recently and marveling over how wild and crazy things are over in Windows-land. A few things related to process creation caught my interest: - It's possible to create a new process with an *arbitrary parent process*, which means it'll then inherit various things like handles and security attributes and tokens from that new parent process. - It's possible to create a new process with the memory space handle of a different process. Consider this on Linux, and you have some abomination like `forkat(int pidfd)`. The big question is "why!?" At first I was just amused by its presence in NT. Everything is an object and you can usually freely mix and match things, and it's very flexible, which is cool. But this is NT, not Linux. Jann and I were discussing, though, that maybe some variant of these features might be useful to get rid of setuid executables. Imagine something like `systemd-sudod`, forked off of PID 1 very early. Subsequently all new processes on the system run with PR_SET_NO_NEW_PRIVS or similar policies to prevent non-root->root transition. Then, if you want to transition, you ask systemd-sudod (or polkitd, or whatever else you have in mind) to make you a new process, and it then does the various policy checks, and executes a new process for you as the parent of the requesting process. So how would that work? Well, executing processes with arbitrary parents would be part of it, as above. But we'd probably want to more carefully control that new process. Which chroot is it in? How do cgroups work? And so on. And ultimately this design leads to something like ZwCreateProcess, where you have several arguments, each to a handle to some part of the new process state, or null to be inherited from its parent. int execve_parent(int parent_pidfd, int root_dirfd, int cgroup_fd, int namespace_fd, const char *pathname, char *const argv[], char *const envp[]); One could imagine this growing pretty unwieldy. There's also this other design aspect of Linux that's worth considering. Namespaces and other process-inherited resources are generally hierarchical, with children getting the resource from their parent. This makes sense and is simple to conceptualize. Everytime we add a new thing_fd as a pointer to one of these resources, and allow it to be used outside of that hierarchy, it introduces a kind of "escape hatch". That might be considered "bad design" by some; it might not be by others. Seen this way, NT is one massive escape hatch, with pretty much everything being an object with a handle. But! Maybe this is nonetheless an interesting design avenue to explore. The introduction of pidfd is sort of just the "beginning" of that kind of design. Is any of this interesting to you as a future of privilege escalation and management on Linux? Jason
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.