Re: caps in elf, next itteration (the hack get's bigger)

Albert D. Cahalan (acahalan@cs.uml.edu)
Thu, 15 Apr 1999 11:26:22 -0400 (EDT)


David L. Parsley writes:
> On Wed, 14 Apr 1999, Pavel Machek wrote:

> If I were designing a secure distribution based on the stickable solution,
> here's how named would start:
>
> Calling process: ( I feel certain this can be easily arranged with the
> 'stickable' solution, and don't want to make this example longer than it
> already is)
> uid=named
> gid=named (only)
> permitted bits=0
> inheritable bits=0
> effective bits=0

Hmmm, looks like you are already playing with the UID.

> Named binary:
> -rwsr-x--T 1 named named 432880 Jan 4 01:07 named*
> permitted bits=CAP_NET_BIND_SERVICE (only)
> inheritable bits=0
> effective bits=CAP_NET_BIND_SERVICE
> extended file attributes=immutable bit set

OK, this has to be ext2. That sucks. You don't have a good backup either.

> named process after exec'ed by calling process: (CAP_NET_BIND_SERVICE
> abbrev. to CAP_NET)
> uid=named
> gid=named (only)
> permitted bits: pP' = fP | ( fI & pI ) = CAP_NET | ( 0 & 0 ) = CAP_NET
> inheritable bits: pI' = pI = 0
> effective bits: pE' = pP' & fE = CAP_NET & CAP_NET = CAP_NET
>
> bash binary:
> -r-xr-xr-T 1 root root 426980 Oct 19 22:57 bash*
> permitted bits=0
> inheritable bits=(all)
> effective bits=(all)
> extended file attributes=immutable bit set
>
> Now, let's say another remotely-exploitable buffer overflow is found in
> named(8). A black hat overflows the buffer, and manages to exec a bash
> shell. Now let's compute the state of the bash shell that's been exec'ed:
>
> bash shell process running parameters:
> uid=named; rights in fs: read rights only to: /etc/named.conf,
> /var/named/*, and /usr/sbin/named; write rights to
> /var/named/(secondaries); execute right to /usr/sbin/named
> gid=named; rights in fs: read and execute /usr/sbin/named
> permitted bits: pP' = fP | ( fI & pI ) = 0 | ( (all) & 0 ) == 0
> inheritable bits: pI' = pI = 0
> effective bits: pE' = pP' & fE = 0 & (all) = 0
>
> So after the attack, the attacker is left only with a shell that can:
> - trash /var/named/(secondaries)
> - re-start named(8), but not modify it in any way.

Why run a shell? The exploit code could transfer an executable and
set any special bits needed. You can not rely on an exec() to drop
bits, since the exec() is not needed.

In case named is unable to set those bits, the exploit code could
just transfer an object file and do a dlopen() on it. Even without
any filesystem access at all, you could bootstrap a huge executable
through a tiny buffer overflow. No exec() ever happens.

> Which, IMHO, is about as secure as you can get. (dunno for sure if it
> can't be improved)
>
> Now, I see three situations possible with setuid0:
> 1) you can mark named(8) setuid root and implement exactly my scenario
> above. I hope you reject the idea of setuid root named as fast as I do.

I reject true setuid root, but not use of the setuid root bit.
Headers may be mangled to prevent execution on old kernels.

If something is dangerous, it ought to look dangerous.
Emacs may already be sticky and immutable.

> 2) you make the named binary have no permitted, and inheritable=CAP_NET,
> with the calling process having only CAP_NET in it's inheritable, and
> otherwise the same as my example. This is your _best_ scenario IMHO, and
> I'll come back to this in a moment.
> 3) You put whatever you want in the cap elf headers, _don't_ mark the file
> 'setuid 0', and interpret your cap elf headers in whatever way you wish,
> but not compliant with the specification for how capabilities operate.
> This would be an abomination IMHO, which I hope you reject.

The specification isn't a Standard, so that would be reasonable.

> already derived this behavior. Also, I beg that you re-consider the
> stickybit+immutable bit solution; it appears you understand well enough
> how it operates. I'm considering trying to modify your patch myself; I
> have the C coding skills, but no experience with kernel data structures
> and API's. I _would_ give it a shot, however.

One could have a mount option like this:

trusted=rootowned,sticky,immutable

By default, setuid-root executables are trusted unless mounting with
setuid executables disabled.

Option list:

rootowned owned by root
setuidroot setuid to root
sticky sticky bit set
immutable immutable bit set
system system bit set
pgp has a valid signature
... ...

One could also add specific trusted executables in the VMS manner.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/