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/