Re: Preventing ptrace()

From: Glynn Clements (glynn.clementsat_private)
Date: Fri Jan 10 2003 - 12:32:52 PST

  • Next message: Crispin Cowan: "Re: PGP scripting..."

    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.clementsat_private>
    



    This archive was generated by hypermail 2b30 : Sat Jan 11 2003 - 12:17:24 PST