Re: Preventing ptrace()

From: Glynn Clements (glynn.clements@virgin.net)
Date: 01/10/03

  • Next message: Crispin Cowan: "Re: PGP scripting..."
    From: Glynn Clements <glynn.clements@virgin.net>
    Date: Fri, 10 Jan 2003 20:32:52 +0000
    To: Timo Sirainen <tss@iki.fi>
    
    

    Timo Sirainen wrote:

    > > > Looks like once a process has called setuid(), no-one except root can
    > > > ptrace() it. I don't see this mentioned very clearly in any man page
    > > > though (*BSD, Linux).
    > >
    > > my ptrace(2) page on debian woody says this:
    > >
    > > ERRORS
    > > EPERM The specified process cannot be traced. This could be because
    > > the parent has insufficient privileges; non-root processes cannot
    > > trace processes that they cannot send signals to or those
    > > running setuid/setgid programs, for obvious reasons.
    > > Alternatively, the process may already be being traced, or be
    > > init (pid 1).
    >
    > You mean the "running setuid/setgid programs"? How is setuid/setgid
    > program defined? I've always thought it was just the +s bit attached to
    > the file. When does the setuidness get cleared; after fork(), exec*(),
    > or ..? Is that standardized somewhere?

    The behaviour of programs with setuid/setgid bits is that executing
    such a program with exec*() results in euid/egid being set to the
    owner/group of the file. The effects persist until the program revokes
    those privileges with setuid(), seteuid() etc, or it executes another
    setuid/setgid program; executing a non-setgid program doesn't revoke
    the privileges.

    However, for the purpose of checking permissions for certain "unsafe"
    operations (e.g. ptrace()), Linux uses a slightly broader definition.
    It includes processes where uid != euid, but also those processes for
    which this was previously true but isn't now. IOW, a process running a
    setuid program remains untraceable even after it drops privileges, as
    it might still have access to privileged resources (e.g. descriptors)
    or have privileged data in memory.

    IIRC, this state ends when exec*() is called (for a non setuid
    program) after any excess privileges have been revoked. This should
    ensure that no privileged data remains in memory (exec*() completely
    replaces the process' memory space, except for the environment/argv
    page), although it's up to the programmer to ensure that any
    privileged resources which would survive exec*() (e.g. descriptors)
    are released first.

    Basically, the intention is that you can only trace "normal"
    processes, i.e. those which you own completely and have always owned
    completely; in case of doubt, permission is refused. However, the
    "abnormal" status must get revoked eventually, otherwise (almost) all
    non-root processes would be abnormal, as they ultimately inherit from
    an initial process (e.g. login, telnetd, sshd) which started as root
    then changed its ownership to that of the user.

    In 2.4.20, the actual permission checks for ptrace() are (from
    kernel/ptrace.c):

            if (task->pid <= 1)
                    goto bad;
            if (task == current)
                    goto bad;
            if (!task->mm)
                    goto bad;
            if(((current->uid != task->euid) ||
                (current->uid != task->suid) ||
                (current->uid != task->uid) ||
                 (current->gid != task->egid) ||
                 (current->gid != task->sgid) ||
                 (!cap_issubset(task->cap_permitted, current->cap_permitted)) ||
                 (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
                    goto bad;
            rmb();
            if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE))
                    goto bad;
            /* the same process cannot be attached many times */
            if (task->ptrace & PT_PTRACED)
                    goto bad;

    However, much of the logic is hidden by the use of task->mm->dumpable;
    IIRC, this determines whether a process can generate core dump, which
    has many of the same security considerations as ptrace(). Examination
    of kernel/sys.c indicates a number of places where this flag is
    cleared (mostly where various uids/gids differ).

    AFAICT, your original comment (that this isn't mentioned clearly in
    any man page) is correct. The above description comes from a mixture
    of comments made in various mailing lists and personal experience.

    -- 
    Glynn Clements <glynn.clements@virgin.net>
    


    Relevant Pages

    • Re: [RCF] [PATCH] unprivileged mount/umount
      ... > If the tracee has different privileges, than the tracer, than it ... I agree this is the proper goal for ptrace and this code is not a hack. ... themselves determine privileges. ... post to which I replied said we have to deal with set-uid programs. ...
      (Linux-Kernel)
    • Re: root user specific commands
      ... The Answer lies in a Concept of setuid. ... any normal user the privilege access of the specified admin related ... Read Write Execute for User and then the next 3 for Group ... root privileges within the created process. ...
      (comp.unix.admin)

  • Quantcast