[prev in list] [next in list] [prev in thread] [next in thread] 

List:       debian-devel
Subject:    Re: setuid/setgid binaries contained in the Debian repository.
From:       Emile van Bergen <emile-deb () evbergen ! xs4all ! nl>
Date:       2003-08-11 16:13:10
[Download RAW message or body]

Hi,

On Mon, Aug 11, 2003 at 10:33:47AM -0400, Matt Zimmerman wrote:

> On Mon, Aug 11, 2003 at 04:00:40PM +0200, Emile van Bergen wrote:
> 
> > On Mon, Aug 11, 2003 at 09:28:42AM -0400, Matt Zimmerman wrote:
> > > setuid results in even more problems than setgid.  Given access to the
> > > game uid, the user can modify the wrapper program (because they own it)
> > > and from that point forward, any user who runs the game is compromised.
> > 
> > The point is that the user doesn't get control over the game uid, because
> > the setuid + wrapper that sets the real uid, etc. provides a barrier to
> > the invoking user. We have to trust such barriers; they are required in
> > the unix design.
> > 
> > If a user could make any setuid binary do arbitrary things, no matter
> > whether it's correctly written, then it's a kernel bug and we are in much,
> > much bigger trouble.
> 
> I don't follow.  The wrapper is running with uid games, and it exec()s the
> actual game.  So the game is running with uid games, exactly as if the game
> itself were setuid, and if the game is exploited, uid games is compromised
> (and so is the wrapper).

If the game is exploited, its uid is exploited, but *only* that uid, not
the user that has invoked it or any other future user that invokes it.

> The only barrier I see is that it would clean the environment variables.
> Yes, this is a popular attack vector, but it is by no means the only one.

No, but it helps. Everything that the wrapper can do will help protect the
game. 

The point is that if the game is *still* compromised, nothing is lost
but the files that can be written by this particular game's uid, which
should be nothing but its own highscore files. Not a problem. A given
game's highscore files need to trust that game anyway.

> > The idea is that the wrapper must be trusted to be able to guarantee
> > dropping all permissions inherited from the invoking user and setting all
> > uids (saved, effective, real) to the per-game uid. After this, the game
> > cannot do anything as the invoking user or to his files. That's the whole
> > point.
> >
> > Setgid is a bigger problem because the process retains the permissions
> > over the invoking users files and gains additional permissions. Not so
> > with this scenario.
> > 
> > After completely switching to the game uid, if mere user input is enough
> > to have the game run arbitrary code under its per-game uid and do the same
> > when the next user runs it, it won't be able to harm that user in any way,
> > simply because that game uid can't do anything to any user at all.
> 
> As I said, the per-game uid would own the wrapper script, and it is far
> easier to replace that with a trojan than to try to exploit the game from
> within.

Good point, but that's easy to fix, and the fix is necessary to get rid
of the user's supplementary groups as well --  it turns out a normal
user cannot trim its own group list using setgroups.

To make the wrapper unwritable both by the user and the per-game
uid/gid, make it setuid root, setgid to the per-game uid/gid, have the
wrapper call setgroups to get rid of the group list, obtain the desired
new uid/gid from the effective gid given by the filesystem, call setgid,
call setuid to set the real, saved and effective uid.

Example:

/etc/passwd:
quake:x:8001:8001:quake

/etc/group:
quake:x:8001:evbergen,mdz,otherplayer

/usr/lib/games:
-rwxr-x---    1 root quake 1234567 Aug 11 16:30 quake (real binary)

/usr/bin:
-rwsr-s---    1 root quake    6231 Aug 11 16:30 quake (from wrapper.c)

wrapper.c:
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <grp.h>

int main(int argc, char **argv)
{
        static char realgame[PATH_MAX];
        static char *gameenv[] = { 0 };
        uid_t gameuidgid;
        char *me, *o;
        size_t melen;

        /* trim supplementary groups; set real, effective, saved uid and
           gid to gameuidgid */

        gameuidgid = getegid();
        if (setgroups(0, NULL) == -1 ||
            setgid(gameuidgid) == -1 ||
            setuid(gameuidgid) == -1) return 1;

        /* securely obtain /usr/lib/games/`basename $0` */

        if (!argv[0]) return 2;
        me = strrchr(argv[0], '/');
        if (me) me++; else me = argv[0];
        melen = strlen(me);
        if (melen < 1 || melen > sizeof(realgame) - 16) return 3;

        o = realgame;
        memcpy(o, "/usr/lib/games/", 15); o += 15;
        memcpy(o, me, melen); o += melen;
        *o++ = 0;

        /* run game */

        execle(realgame, realgame, 0, gameenv);
        return 4;
}

Any user that's a member of the group 'quake' can run /usr/lib/games/quake as 
user 'quake', group 'quake', nothing else. Neither the user nor 'quake' can
write to the wrapper.

Now, if this is insecure for any reason, I'd like to hear why, because then
it seems unix has a fundamental problem. 

Cheers,


Emile.

-- 
E-Advies - Emile van Bergen           emile@e-advies.nl      
tel. +31 (0)70 3906153           http://www.e-advies.nl    

[Attachment #3 (application/pgp-signature)]
-- 
To UNSUBSCRIBE, email to debian-devel-request@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmaster@lists.debian.org


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic