Lennart Poettering writes:
On Fr, 28.01.22 18:16, Sam Varshavchik (mrsam(a)courier-mta.com)
wrote:
> Having said all of that: the suid bit itself is irrelevant. It is nothing
> more than a convenient scapegoat to blame other bugs on. The same bug
that's
> exploitable in a suid binary will also be exploitable, exactly the same
way,
> in its suid-less equivalent. If you have a buffer overrun in a suid binary
> as a result of carefully-crafted command-line parameters or environment,
> then if you replace the suid binary with an identical bug-for-bug
> implementation that uses a socket to carefully pass along the same
> environment or parameters to a native root binary, and the buffer overrun
is
> the same, guess what: you still have the same exploit.
I vehemently disagree. The thing with setuid/setgid is that the
invoked privileged process inherits a lot of implicit state and
context that people aren't really aware of or fully
understand. i.e. it's not just env vars and argv[], it's cgroup
memberships, audit fields, security contexts, open fds, child pids,
parent pids, cpu masks, IO/CPU scheduling priorities, various prctl()
settings, tty control, signal masks + handlers, … and so on.
And none of that makes any difference, whatsoever, unless there's also an
underlying logical bug in the code itself. And that's going to be the real
problem. So then, question becomes, how many exploits leveraged any of these
exotic resources, like cgroup members, tty control, and all of those other
fringe attributes? I suppose there might've been a few, but I can't recall
many. On the other hand, exploits that accomplish execve("/bin/bash") would
work equally well, given the same underlying bug in the root daemon, that's
triggerable via its front end, or in a suid program. A sanitized environment
won't be much of a help.
Give me a suid program doing a fgets() with a wrong buffer size on a stack
array. Then also give me a program that connects to a root daemon over a
socket, then simply forwards all input to it, with the root daemon also
doing the same exact buffer overrun. The exploit will be the same. The root
daemon's pristine, pure as wind-driven snow, environment won't make any
difference.
In fact, having extra moving pieces only breeds more opportunities for bugs.
There are still too many simpletons who are going to conclude that the right
way to go is by doing all edit checking in the wrapper, then passing
validated data to the daemon who'll assume its validity.
"The more you overthink the plumbing, the easier it is to stop up the
drain." – Scotty, Star Trek III.
> suid is not the problem. An execved program will inherit the
environment,
> some open file descriptors, and maybe a few other things that a standalone
> daemon that accepted a socket connection does not have. But that's what
most
> exploits leverage, so cleaning up the environment and open file descriptors
> would remedy that. It will take some effort to exploit whatever
> remains.
IRL you cannot clean up your execution context, because new stuff is
added all the time. And often enough it's not even clear whar reset it
to, e.g. cgroup stuff.
Stuff that's "added all the time" won't be used in existing code base,
and
will be immaterial. None of the stuff from semi-recent history (containers,
CPU affinities, etc…) appears to have much effect on my existing setup.