Re: [PATCH][2.4] cleanup ptrace secfix and fix most side effects

Bernhard Kaindl (bk@suse.de)
Fri, 9 May 2003 01:59:46 +0200 (CEST)


On 8 May 2003, Alan Cox wrote:
> On Iau, 2003-05-08 at 23:05, Bernhard Kaindl wrote:
> > - mb();
> > - if (!is_dumpable(child))
> > - return -EPERM;
> >
> > if (!(child->ptrace & PT_PTRACED))
> > return -ESRCH;
> >
> > Using is_dumpable() here is not neccesary because the checks done here are:
> >
> > > if (!(child->ptrace & PT_PTRACED))
> > > return -ESRCH;
>
> Except that current->mm->dumpable is not per task but per mm so you
> might ptrace one thread and have another go setuid.

You might try, but as far as I know:

a) setuid requires execve() which decouples from the other thread
and also gives the new thread a newly allocated task->mm.

b) If the thread which calls execve() is being traced, execve ignores
setuid.

c) If the thread which calls execve() is being not traced, a tracer has
to attach first, otherwise

> > > if (!(child->ptrace & PT_PTRACED))
> > > return -ESRCH;

catches the agaist-the-API ptrace call and the only way to set

child->ptrace & PT_PTRACED

is to call ptrace_attach() which checks for matching uids/gids
beween tracer and the setuid task and requiring that the tracer
may not miss a capability which the setuid task has granted and
effectively denies ptrace access otherwise:

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;

d) Even if the setuid program changes uids and capabilies back so that
you would pass the check above, you will fail in the next line here:

if (!is_dumpable(task) && !capable(CAP_SYS_PTRACE))
goto bad;

And you can't change task->mm->dumpable back to 1 because the new
task has got a new mm allocated to which you have no access.

So, unless you have CAP_SYS_PTRACE, you will fail to trace the setuid
program(CAP_SYS_PTRACE is documented to allow to trace setuid) unless
it does prctl(PR_SET_DUMPABLE, 1) and after giving all capabilies gained
away and setting uid and gids back. At this point you could attach to
it again, but by calling prctl(PR_SET_DUMPABLE, 1), the setudi program
decares that it does not have any sensitive data anymore because you
could also send him a signal to cause him core dumping and read the
core with emacs then.

Do you see any chance to gain anything this way or
do you mean a scenario which I did not describe here?

Thanks,
Bernd

PS:

Just in case if people are interested where execve() gets a new mm
for the new program:

execve() calls do_execve()
which calls the binfmt's file loader(e.g. load_elf_binary)
which calls flush_old_exec()
which calls exec_mmap()
which releases the old mm and allocates a
new task->mm for the new process:

fs/exec.c, exec_mmap():

old_mm = current->mm;
if (old_mm) {
rlimit_rss = old_mm->rlimit_rss;
if (atomic_read(&old_mm->mm_users) == 1) {
mm_release();
exit_aio(old_mm);
exit_mmap(old_mm);
return 0;
}
}

mm = mm_alloc();
[...]
current->mm = mm;

EOF

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