Re: COSEINC Linux Advisory #1: Linux Kernel Parent Process Death Signal Vulnerability
On Thu, 16 Aug 2007, Glynn Clements wrote:
> > The signal in question in the given situation is issued by PRIVILEGED
> > process,
> > no matter how.
>
> And that's the bug,
The case we consider is of course a bug. But generally privileged process
sending a signal to another privileged process is of course not a bug.
Yes, the user toggles a signal that privileged process sends to another one,
but how many ways to trigger sending a signal to a process spawned by that user
are there? User can always press Ctrl-C, Ctrl-Q, Ctrl-R at the terminal, he can
hang up the terminal, he can open some socket/pipe/FIFO for non-blocking I/O
and issue fcntl() call to activate sending either a SIGIO or some other signal
when some new data become available in it, he can issue alarm(), and probably
many, many, many other ways. Yes, all they can be turned off, but that would
require a large artificial intelligence and excessive knowledge about
everything occurred in the system before startup from the program being
started. It's much simpler for that program to just block, ignore or install
proper signal handler for every possible signal in the system whose default
action is terminating a process.
> because it's an unprivileged process which
> *causes* the signal to be issued. If the process tried to send the
> signal directly with kill(), it would fail. This vulnerability is a
> form of privilege escalation; it can cause the sending of signal which
> would normally be prohibited on security grounds.
>
The matter here is that you cannot abuse this bug to send arbitrary signal to
arbitrary process in the system. You can only send it to your children. And
this is in general not exploitable. For example, what scary things can happen
when you send SIGKILL, SIGQUIT, SIGBUS, SIGSTOP or SIGSEGV to /bin/su in
condition that the latter doesn't allow arbitrary code execution on receiving a
signal?
> > Well written program must not depend on anything that is out of
> > it's control.
>
> That's neither possible
Really? Let's consider the following scenario. You write an analogue of
/bin/passwd. Here you make a temporary copy of /etc/shadow, hard link
/etc/shadow to /etc/shadow- pre-removing existing /etc/shadow- if that exists,
perform operations on the temporary copy of /etc/shadow, close it and issue
rename() on it to rename it to /etc/shadow. According to specs (see rename(2))
rename() atomically removes /etc/shadow (at this point it is an old link to an
old /etc/shadow content) and renames temporary copy of /etc/shadow to
/etc/shadow. At any point in this algorithm the content of /etc/shadow is
consistent.
> nor sensible.
If it is not sensible, it is the more not a problem.
> Programs have to rely upon the
> OS to guarantee certain behaviours. The problem here is that there is
> a mechanism which causes a guarantee to be violated.
>
Yes, and I said this is a bug, but it is in general not exploitable.
> Just in case it hasn't sunk in yet, the inability to trust signals is
> a consequence of this bug. Ordinarily, it should be possible to rely
> upon the fact that an asynchronous signal cannot be sent to a suid
> process by an unprivileged user.
>
I disagree with you in that. Any hard guarantee can be given only by God.
I repeat, signals are in general not a reliable information source since they
can be generated in a couple of ways, even by an unkind superuser :-) .
> > In fact, PDEATHSIG should be reset for every binary, not just suid/sgid,
> > since
> > it emits signal that exec()ed program may not expect.
>
> Are you talking about the parent exec()ing or the child?
>
No matter.
> If you're talking about the child, that would almost entirely defeat
> the purpose of having PDEATHSIG.
>
The only useful exploitation of PDEATHSIG I can imagine is signalling to a
server subprocess that it's superprocess died for some reason and the
subprocess should exit as far as possible in order to avoid some harm. That
model doesn't include execing.
> > But in any case, every program shouldn't trust any signal in the
> > system. That is a good tone rule.
>
> In which case, what's the point of having signals in the first place?
>
> Processes are supposed to respond to signals. Security is achieved
> placing controls on who can signal who, and this bug circumvents that
> mechanism.
>
But the process in general is in no way obliged to respond to every signal
unless explicitly wanted.
> > I still don't see why this bug should be considered as a security issue but
> > not
> > as an ordinary bug.
>
> Because it's a form of privilege escalation. Non-root processes can't
> normally send signals to processes which are owned by another UID (and
> most modern operating systems prevent non-root processes from sending
> signals to any process where suid/sgid is involved regardless of the
> current UID or EUID).
>
I repeat, this bug cannot be abused to send arbitrary signal to arbitrary
process in the system. Only direct successors (children) are affected, and this
is in general not exploitable.
> > > Moreover, I would suggest that exec()ing a suid/sgid binary should
> > > reset *everything* which is not explicitly specified as being
> > > preserved.
> >
> > Specified with what?
>
> POSIX, XPG, SUS.
>
I got you. I just thought you want to reset everything successively including
sigmasks and open files :-) .
--
Sincerely Your, Dan.