• Andrew Morton's avatar
    [PATCH] compute_creds race · b7fbe52c
    Andrew Morton authored
    From: Andy Lutomirski <luto@myrealbox.com>
    
    Fixes from me, Olaf Dietsche <olaf+list.linux-kernel@olafdietsche.de>
    
    In fs/exec.c, compute_creds does:
    
    	task_lock(current);
    	if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) {
                     current->mm->dumpable = 0;
    
    		if (must_not_trace_exec(current)
    		    || atomic_read(&current->fs->count) > 1
    		    || atomic_read(&current->files->count) > 1
    		    || atomic_read(&current->sighand->count) > 1) {
    			if(!capable(CAP_SETUID)) {
    				bprm->e_uid = current->uid;
    				bprm->e_gid = current->gid;
    			}
    		}
    	}
    
             current->suid = current->euid = current->fsuid = bprm->e_uid;
             current->sgid = current->egid = current->fsgid = bprm->e_gid;
    
    	task_unlock(current);
    
    	security_bprm_compute_creds(bprm);
    
    I assume the task_lock is to prevent another process (on SMP or preempt)
    from ptracing the execing process between the check and the assignment.  If
    that's the concern then the fact that the lock is dropped before the call
    to security_brpm_compute_creds means that, if security_bprm_compute_creds
    does anything interesting, there's a race.
    
    For my (nearly complete) caps patch, I obviously need to fix this.  But I
    think it may be exploitable now.  Suppose there are two processes, A (the
    malicious code) and B (which uses exec).  B starts out unprivileged (A and
    B have, e.g., uid and euid = 500).
    
    1. A ptraces B.
    
    2. B calls exec on some setuid-root program.
    
    3. in cap_bprm_set_security, B sets bprm->cap_permitted to the full
       set.
    
    4. B gets to compute_creds in exec.c, calls task_lock, and does not
       change its uid.
    
    5. B calls task_unlock.
    
    6. A detaches from B (on preempt or SMP).
    
    7. B gets to task_lock in cap_bprm_compute_creds, changes its
       capabilities, and returns from compute_creds into load_elf_binary.
    
    8. load_elf_binary calls create_elf_tables (line 852 in 2.6.5-mm1),
       which calls cap_bprm_secureexec (through LSM), which returns false (!).
    
    9. exec finishes.
    
    The setuid program is now running with uid=euid=500 but full permitted
    capabilities.  There are two (or three) ways to effectively get local root
    now:
    
    1.  IIRC, linux 2.4 doesn't check capabilities in ptrace, so A could
       just ptrace B again.
    
    2. LD_PRELOAD.
    
    3.  There are probably programs that will misbehave on their own under
       these circumstances.
    
    Is there some reason why this is not doable?
    
    The patch renames bprm_compute_creds to bprm_apply_creds and moves all uid
    logic into the hook, where the test and the resulting modification can both
    happen under task_lock().
    
    This way, out-of-tree LSMs will fail to compile instead of malfunctioning. 
    It should also make life easier for LSMs and will certainly make it easier
    for me to finish the cap patch.
    b7fbe52c
exec.c 31.4 KB